From e3e21acb23283915c97e6ef1c167325f4592665c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 11 Oct 2019 20:55:59 +0200 Subject: libgnunetpq API change to fix #5733 --- src/datacache/plugin_datacache_postgres.c | 25 +--- src/datastore/plugin_datastore_postgres.c | 20 +--- src/include/gnunet_pq_lib.h | 133 ++++++++++++++------- src/namecache/plugin_namecache_postgres.c | 38 ++---- src/namestore/plugin_namestore_postgres.c | 64 +++------- src/pq/Makefile.am | 2 +- src/pq/pq.c | 43 ++++--- src/pq/pq.h | 57 +++++++++ src/pq/pq_connect.c | 169 ++++++++++++++++++++++----- src/pq/pq_eval.c | 59 ++++++---- src/pq/pq_exec.c | 15 ++- src/pq/pq_prepare.c | 39 +++++-- src/pq/pq_result_helper.c | 2 +- src/pq/test_pq.c | 186 +++++++++++++++--------------- 14 files changed, 517 insertions(+), 335 deletions(-) create mode 100644 src/pq/pq.h diff --git a/src/datacache/plugin_datacache_postgres.c b/src/datacache/plugin_datacache_postgres.c index 59dff9067..c532550ae 100644 --- a/src/datacache/plugin_datacache_postgres.c +++ b/src/datacache/plugin_datacache_postgres.c @@ -48,7 +48,7 @@ struct Plugin /** * Native Postgres database handle. */ - PGconn *dbh; + struct GNUNET_PQ_Context *dbh; /** * Number of key-value pairs in the database. @@ -122,26 +122,11 @@ init_connection (struct Plugin *plugin) }; plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg, - "datacache-postgres"); + "datacache-postgres", + es, + ps); if (NULL == plugin->dbh) return GNUNET_SYSERR; - if (GNUNET_OK != - GNUNET_PQ_exec_statements (plugin->dbh, - es)) - { - PQfinish (plugin->dbh); - plugin->dbh = NULL; - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_PQ_prepare_statements (plugin->dbh, - ps)) - { - PQfinish (plugin->dbh); - plugin->dbh = NULL; - return GNUNET_SYSERR; - } return GNUNET_OK; } @@ -710,7 +695,7 @@ libgnunet_plugin_datacache_postgres_done (void *cls) struct GNUNET_DATACACHE_PluginFunctions *api = cls; struct Plugin *plugin = api->cls; - PQfinish (plugin->dbh); + GNUNET_PQ_disconnect (plugin->dbh); GNUNET_free (plugin); GNUNET_free (api); return NULL; diff --git a/src/datastore/plugin_datastore_postgres.c b/src/datastore/plugin_datastore_postgres.c index 181cf8cf8..0811edfd7 100644 --- a/src/datastore/plugin_datastore_postgres.c +++ b/src/datastore/plugin_datastore_postgres.c @@ -54,7 +54,7 @@ struct Plugin /** * Native Postgres database handle. */ - PGconn *dbh; + struct GNUNET_PQ_Context *dbh; }; @@ -172,21 +172,11 @@ init_connection (struct Plugin *plugin) #undef RESULT_COLUMNS plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg, - "datastore-postgres"); + "datastore-postgres", + es, + ps); if (NULL == plugin->dbh) return GNUNET_SYSERR; - - if ((GNUNET_OK != - GNUNET_PQ_exec_statements (plugin->dbh, - es)) || - (GNUNET_OK != - GNUNET_PQ_prepare_statements (plugin->dbh, - ps))) - { - PQfinish (plugin->dbh); - plugin->dbh = NULL; - return GNUNET_SYSERR; - } return GNUNET_OK; } @@ -974,7 +964,7 @@ libgnunet_plugin_datastore_postgres_done (void *cls) struct GNUNET_DATASTORE_PluginFunctions *api = cls; struct Plugin *plugin = api->cls; - PQfinish (plugin->dbh); + GNUNET_PQ_disconnect (plugin->dbh); GNUNET_free (plugin); GNUNET_free (api); return NULL; diff --git a/src/include/gnunet_pq_lib.h b/src/include/gnunet_pq_lib.h index 6c576c8ab..a56df21fd 100644 --- a/src/include/gnunet_pq_lib.h +++ b/src/include/gnunet_pq_lib.h @@ -46,15 +46,16 @@ * @param scratch_length number of entries left in @a scratch * @return -1 on error, number of offsets used in @a scratch otherwise */ -typedef int (*GNUNET_PQ_QueryConverter) (void *cls, - const void *data, - size_t data_len, - void *param_values[], - int param_lengths[], - int param_formats[], - unsigned int param_length, - void *scratch[], - unsigned int scratch_length); +typedef int +(*GNUNET_PQ_QueryConverter) (void *cls, + const void *data, + size_t data_len, + void *param_values[], + int param_lengths[], + int param_formats[], + unsigned int param_length, + void *scratch[], + unsigned int scratch_length); /** @@ -214,12 +215,13 @@ GNUNET_PQ_query_param_uint64 (const uint64_t *x); * #GNUNET_YES if all results could be extracted * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) */ -typedef int (*GNUNET_PQ_ResultConverter) (void *cls, - PGresult *result, - int row, - const char *fname, - size_t *dst_size, - void *dst); +typedef int +(*GNUNET_PQ_ResultConverter) (void *cls, + PGresult *result, + int row, + const char *fname, + size_t *dst_size, + void *dst); /** @@ -229,7 +231,9 @@ typedef int (*GNUNET_PQ_ResultConverter) (void *cls, * @param cls closure * @param rd result data to clean up */ -typedef void (*GNUNET_PQ_ResultCleanup) (void *cls, void *rd); +typedef void +(*GNUNET_PQ_ResultCleanup) (void *cls, + void *rd); /** @@ -419,17 +423,23 @@ GNUNET_PQ_result_spec_uint64 (const char *name, uint64_t *u64); /* ************************* pq.c functions ************************ */ +/** + * Postgres context. + */ +struct GNUNET_PQ_Context; + + /** * Execute a prepared statement. * - * @param db_conn database connection + * @param db database context * @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, +GNUNET_PQ_exec_prepared (struct GNUNET_PQ_Context *db, const char *name, const struct GNUNET_PQ_QueryParam *params); @@ -468,7 +478,7 @@ GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs); * Check the @a result's error code to see what happened. * Also logs errors. * - * @param connection connection to execute the statement in + * @param db database 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 @@ -478,7 +488,7 @@ GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs); * @deprecated (low level, let's see if we can do with just the high-level functions) */ enum GNUNET_DB_QueryStatus -GNUNET_PQ_eval_result (PGconn *connection, +GNUNET_PQ_eval_result (struct GNUNET_PQ_Context *db, const char *statement_name, PGresult *result); @@ -488,7 +498,7 @@ GNUNET_PQ_eval_result (PGconn *connection, * statement in @a connnection using the given @a params. Returns the * resulting session state. * - * @param connection connection to execute the statement in + * @param db database to execute the statement with * @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 @@ -500,7 +510,7 @@ GNUNET_PQ_eval_result (PGconn *connection, * zero; if INSERT was successful, we return one. */ enum GNUNET_DB_QueryStatus -GNUNET_PQ_eval_prepared_non_select (PGconn *connection, +GNUNET_PQ_eval_prepared_non_select (struct GNUNET_PQ_Context *db, const char *statement_name, const struct GNUNET_PQ_QueryParam *params); @@ -513,9 +523,10 @@ GNUNET_PQ_eval_prepared_non_select (PGconn *connection, * @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); +typedef void +(*GNUNET_PQ_PostgresResultHandler) (void *cls, + PGresult *result, + unsigned int num_results); /** @@ -525,7 +536,7 @@ typedef void (*GNUNET_PQ_PostgresResultHandler) (void *cls, * 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 db database to execute the statement with * @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 @@ -534,7 +545,7 @@ typedef void (*GNUNET_PQ_PostgresResultHandler) (void *cls, * codes to `enum GNUNET_DB_QueryStatus`. */ enum GNUNET_DB_QueryStatus -GNUNET_PQ_eval_prepared_multi_select (PGconn *connection, +GNUNET_PQ_eval_prepared_multi_select (struct GNUNET_PQ_Context *db, const char *statement_name, const struct GNUNET_PQ_QueryParam *params, GNUNET_PQ_PostgresResultHandler rh, @@ -549,7 +560,7 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection, * value was #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT. Returns the * resulting session status. * - * @param connection connection to execute the statement in + * @param db database to execute the statement with * @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 @@ -557,11 +568,11 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection, * codes to `enum GNUNET_DB_QueryStatus`. */ enum GNUNET_DB_QueryStatus -GNUNET_PQ_eval_prepared_singleton_select ( - PGconn *connection, - const char *statement_name, - const struct GNUNET_PQ_QueryParam *params, - struct GNUNET_PQ_ResultSpec *rs); +GNUNET_PQ_eval_prepared_singleton_select (struct GNUNET_PQ_Context *db, + const char *statement_name, + const struct + GNUNET_PQ_QueryParam *params, + struct GNUNET_PQ_ResultSpec *rs); /* ******************** pq_prepare.c functions ************** */ @@ -587,6 +598,7 @@ struct GNUNET_PQ_PreparedStatement * Number of arguments included in @e sql. */ unsigned int num_arguments; + }; @@ -616,14 +628,14 @@ GNUNET_PQ_make_prepare (const char *name, /** * Request creation of prepared statements @a ps from Postgres. * - * @param connection connection to prepare the statements for + * @param db database 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, +GNUNET_PQ_prepare_statements (struct GNUNET_PQ_Context *db, const struct GNUNET_PQ_PreparedStatement *ps); @@ -681,14 +693,14 @@ 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 pq database to execute the statements in * @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, +GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db, const struct GNUNET_PQ_ExecuteStatement *es); @@ -698,26 +710,61 @@ GNUNET_PQ_exec_statements (PGconn *connection, /** * 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. + * routines and disable Postgres's logger. Also ensures that the + * statements in @a es are executed whenever we (re)connect to the + * database, and that the prepared statements in @a ps are "ready". + * If statements in @es fail that were created with + * #GNUNET_PQ_make_execute(), then the entire operation fails. * * @param config_str configuration to use + * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated + * array of statements to execute upon EACH connection, can be NULL + * @param ps array of prepared statements to prepare, can be NULL * @return NULL on error */ -PGconn * -GNUNET_PQ_connect (const char *config_str); +struct GNUNET_PQ_Context * +GNUNET_PQ_connect (const char *config_str, + const struct GNUNET_PQ_ExecuteStatement *es, + const struct GNUNET_PQ_PreparedStatement *ps); /** * Connect to a postgres database using the configuration - * option "CONFIG" in @a section. + * option "CONFIG" in @a section. Also ensures that the + * statements in @a es are executed whenever we (re)connect to the + * database, and that the prepared statements in @a ps are "ready". * * @param cfg configuration * @param section configuration section to use to get Postgres configuration options + * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated + * array of statements to execute upon EACH connection, can be NULL + * @param ps array of prepared statements to prepare, can be NULL * @return the postgres handle, NULL on error */ -PGconn * +struct GNUNET_PQ_Context * GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *section); + const char *section, + const struct GNUNET_PQ_ExecuteStatement *es, + const struct GNUNET_PQ_PreparedStatement *ps); + + +/** + * Reinitialize the database @a db. + * + * @param db database connection to reinitialize + */ +void +GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db); + + +/** + * Disconnect from the database, destroying the prepared statements + * and releasing other associated resources. + * + * @param db database handle to disconnect (will be free'd) + */ +void +GNUNET_PQ_disconnect (struct GNUNET_PQ_Context *db); #endif /* GNUNET_PQ_LIB_H_ */ diff --git a/src/namecache/plugin_namecache_postgres.c b/src/namecache/plugin_namecache_postgres.c index e4b360ef2..35bf5c2ff 100644 --- a/src/namecache/plugin_namecache_postgres.c +++ b/src/namecache/plugin_namecache_postgres.c @@ -42,9 +42,9 @@ struct Plugin const struct GNUNET_CONFIGURATION_Handle *cfg; /** - * Native Postgres database handle. + * Postgres database handle. */ - PGconn *dbh; + struct GNUNET_PQ_Context *dbh; }; @@ -75,10 +75,6 @@ database_setup (struct Plugin *plugin) "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 == GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, "namecache-postgres", @@ -90,7 +86,6 @@ database_setup (struct Plugin *plugin) { cr = &es_default; } - { struct GNUNET_PQ_ExecuteStatement es[] = { *cr, @@ -100,18 +95,6 @@ database_setup (struct Plugin *plugin) "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; - } - } - - { struct GNUNET_PQ_PreparedStatement ps[] = { GNUNET_PQ_make_prepare ("cache_block", "INSERT INTO ns096blocks (query, block, expiration_time) VALUES " @@ -128,16 +111,13 @@ database_setup (struct Plugin *plugin) GNUNET_PQ_PREPARED_STATEMENT_END }; - if (GNUNET_OK != - GNUNET_PQ_prepare_statements (plugin->dbh, - ps)) - { - PQfinish (plugin->dbh); - plugin->dbh = NULL; - return GNUNET_SYSERR; - } + plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg, + "namecache-postgres", + es, + ps); } - + if (NULL == plugin->dbh) + return GNUNET_SYSERR; return GNUNET_OK; } @@ -311,7 +291,7 @@ namecache_postgres_lookup_block (void *cls, static void database_shutdown (struct Plugin *plugin) { - PQfinish (plugin->dbh); + GNUNET_PQ_disconnect (plugin->dbh); plugin->dbh = NULL; } diff --git a/src/namestore/plugin_namestore_postgres.c b/src/namestore/plugin_namestore_postgres.c index 5148ca0f5..23893538b 100644 --- a/src/namestore/plugin_namestore_postgres.c +++ b/src/namestore/plugin_namestore_postgres.c @@ -45,9 +45,9 @@ struct Plugin const struct GNUNET_CONFIGURATION_Handle *cfg; /** - * Native Postgres database handle. + * Postgres database handle. */ - PGconn *dbh; + struct GNUNET_PQ_Context *dbh; }; @@ -88,30 +88,8 @@ database_setup (struct Plugin *plugin) ")" "WITH OIDS"); const struct GNUNET_PQ_ExecuteStatement *cr; + struct GNUNET_PQ_ExecuteStatement sc = GNUNET_PQ_EXECUTE_STATEMENT_END; - plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg, - "namestore-postgres"); - if (NULL == plugin->dbh) - return GNUNET_SYSERR; - if (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, - "namestore-postgres", - "ASYNC_COMMIT")) - { - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ("SET synchronous_commit TO off"), - 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 (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, "namestore-postgres", @@ -124,6 +102,12 @@ database_setup (struct Plugin *plugin) cr = &es_default; } + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, + "namestore-postgres", + "ASYNC_COMMIT")) + sc = GNUNET_PQ_make_try_execute ("SET synchronous_commit TO off"); + { struct GNUNET_PQ_ExecuteStatement es[] = { *cr, @@ -135,20 +119,9 @@ database_setup (struct Plugin *plugin) "ON ns098records (label)"), GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS zone_label " "ON ns098records (zone_private_key,label)"), + sc, GNUNET_PQ_EXECUTE_STATEMENT_END }; - - if (GNUNET_OK != - GNUNET_PQ_exec_statements (plugin->dbh, - es)) - { - PQfinish (plugin->dbh); - plugin->dbh = NULL; - return GNUNET_SYSERR; - } - } - - { struct GNUNET_PQ_PreparedStatement ps[] = { GNUNET_PQ_make_prepare ("store_records", "INSERT INTO ns098records" @@ -183,16 +156,13 @@ database_setup (struct Plugin *plugin) GNUNET_PQ_PREPARED_STATEMENT_END }; - if (GNUNET_OK != - GNUNET_PQ_prepare_statements (plugin->dbh, - ps)) - { - PQfinish (plugin->dbh); - plugin->dbh = NULL; - return GNUNET_SYSERR; - } + plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg, + "namestore-postgres", + es, + ps); } - + if (NULL == plugin->dbh) + return GNUNET_SYSERR; return GNUNET_OK; } @@ -593,7 +563,7 @@ namestore_postgres_zone_to_name (void *cls, static void database_shutdown (struct Plugin *plugin) { - PQfinish (plugin->dbh); + GNUNET_PQ_disconnect (plugin->dbh); plugin->dbh = NULL; } diff --git a/src/pq/Makefile.am b/src/pq/Makefile.am index 9270e6fe0..750a1d48d 100644 --- a/src/pq/Makefile.am +++ b/src/pq/Makefile.am @@ -22,7 +22,7 @@ libgnunetpq_la_LIBADD = -lpq \ libgnunetpq_la_LDFLAGS = \ $(POSTGRESQL_LDFLAGS) \ $(GN_LIB_LDFLAGS) \ - -version-info 0:0:0 + -version-info 1:0:0 if ENABLE_TEST_RUN TESTS = \ diff --git a/src/pq/pq.c b/src/pq/pq.c index 7e97c8f72..d2b9a6174 100644 --- a/src/pq/pq.c +++ b/src/pq/pq.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2014, 2015, 2016 GNUnet e.V. + Copyright (C) 2014, 2015, 2016, 2017, 2019 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -25,33 +25,30 @@ * @author Christian Grothoff */ #include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_pq_lib.h" - +#include "pq.h" /** * Execute a prepared statement. * - * @param db_conn database connection + * @param db database handle * @param name name of the prepared statement * @param params parameters to the statement * @return postgres result */ PGresult * -GNUNET_PQ_exec_prepared (PGconn *db_conn, +GNUNET_PQ_exec_prepared (struct GNUNET_PQ_Context *db, const char *name, const struct GNUNET_PQ_QueryParam *params) { unsigned int len; - unsigned int i; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running prepared statement `%s' on %p\n", name, - db_conn); + db); /* count the number of parameters */ len = 0; - for (i = 0; 0 != params[i].num_params; i++) + for (unsigned int i = 0; 0 != params[i].num_params; i++) len += params[i].num_params; /* new scope to allow stack allocation without alloca */ @@ -67,10 +64,11 @@ GNUNET_PQ_exec_prepared (PGconn *db_conn, unsigned int soff; PGresult *res; int ret; + ConnStatusType status; off = 0; soff = 0; - for (i = 0; 0 != params[i].num_params; i++) + for (unsigned int i = 0; 0 != params[i].num_params; i++) { const struct GNUNET_PQ_QueryParam *x = ¶ms[i]; @@ -97,13 +95,24 @@ GNUNET_PQ_exec_prepared (PGconn *db_conn, "pq", "Executing prepared SQL statement `%s'\n", name); - res = PQexecPrepared (db_conn, + res = PQexecPrepared (db->conn, name, len, (const char **) param_values, param_lengths, param_formats, 1); + if ( (PGRES_COMMAND_OK != PQresultStatus (res)) && + (CONNECTION_OK != (status = PQstatus (db->conn))) ) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "pq", + "Database disconnected on SQL statement `%s' (reconnecting)\n", + name); + GNUNET_PQ_reconnect (db); + res = NULL; + } + for (off = 0; off < soff; off++) GNUNET_free (scratch[off]); return res; @@ -120,9 +129,7 @@ GNUNET_PQ_exec_prepared (PGconn *db_conn, void GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs) { - unsigned int i; - - for (i = 0; NULL != rs[i].conv; i++) + for (unsigned int i = 0; NULL != rs[i].conv; i++) if (NULL != rs[i].cleaner) rs[i].cleaner (rs[i].cls, rs[i].dst); @@ -145,12 +152,12 @@ GNUNET_PQ_extract_result (PGresult *result, struct GNUNET_PQ_ResultSpec *rs, int row) { - unsigned int i; - int ret; - - for (i = 0; NULL != rs[i].conv; i++) + if (NULL == result) + return GNUNET_SYSERR; + for (unsigned int i = 0; NULL != rs[i].conv; i++) { struct GNUNET_PQ_ResultSpec *spec; + int ret; spec = &rs[i]; ret = spec->conv (spec->cls, diff --git a/src/pq/pq.h b/src/pq/pq.h new file mode 100644 index 000000000..b30f4f0d4 --- /dev/null +++ b/src/pq/pq.h @@ -0,0 +1,57 @@ +/* + This file is part of GNUnet + Copyright (C) 2017, 2019 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file pq/pq.h + * @brief shared internal data structures of libgnunetpq + * @author Christian Grothoff + */ +#ifndef PQ_H +#define PQ_H + +#include "gnunet_util_lib.h" +#include "gnunet_pq_lib.h" + +/** + * Handle to Postgres database. + */ +struct GNUNET_PQ_Context +{ + /** + * Actual connection. + */ + PGconn *conn; + + /** + * Statements to execute upon connection. + */ + struct GNUNET_PQ_ExecuteStatement *es; + + /** + * Prepared statements. + */ + struct GNUNET_PQ_PreparedStatement *ps; + + /** + * Configuration to use to connect to the DB. + */ + char *config_str; +}; + +#endif diff --git a/src/pq/pq_connect.c b/src/pq/pq_connect.c index 79b9d6107..7599f4b15 100644 --- a/src/pq/pq_connect.c +++ b/src/pq/pq_connect.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2017 GNUnet e.V. + Copyright (C) 2017, 2019 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -23,8 +23,7 @@ * @author Christian Grothoff */ #include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_pq_lib.h" +#include "pq.h" /** @@ -40,12 +39,14 @@ pq_notice_receiver_cb (void *arg, const PGresult *res) { /* do nothing, intentionally */ + (void) arg; + (void) res; } /** * Function called by libpq whenever it wants to log something. - * We log those using the Taler logger. + * We log those using the GNUnet logger. * * @param arg the SQL connection that was used * @param message information about some libpq event @@ -54,6 +55,7 @@ static void pq_notice_processor_cb (void *arg, const char *message) { + (void) arg; GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "pq", "%s", @@ -64,68 +66,175 @@ pq_notice_processor_cb (void *arg, /** * 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. + * routines and disable Postgres's logger. Also ensures that the + * statements in @a es are executed whenever we (re)connect to the + * database, and that the prepared statements in @a ps are "ready". + * If statements in @es fail that were created with + * #GNUNET_PQ_make_execute(), then the entire operation fails. + * + * The caller MUST ensure that @a es and @a ps remain allocated and + * initialized in memory until #GNUNET_PQ_disconnect() is called, + * as they may be needed repeatedly and no copy will be made. * * @param config_str configuration to use + * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated + * array of statements to execute upon EACH connection, can be NULL + * @param ps array of prepared statements to prepare, can be NULL * @return NULL on error */ -PGconn * -GNUNET_PQ_connect (const char *config_str) +struct GNUNET_PQ_Context * +GNUNET_PQ_connect (const char *config_str, + const struct GNUNET_PQ_ExecuteStatement *es, + const struct GNUNET_PQ_PreparedStatement *ps) { - PGconn *conn; + struct GNUNET_PQ_Context *db; + unsigned int elen = 0; + unsigned int plen = 0; + + if (NULL != es) + while (NULL != es[elen].sql) + elen++; + if (NULL != ps) + while (NULL != ps[plen].name) + plen++; + + db = GNUNET_new (struct GNUNET_PQ_Context); + db->config_str = GNUNET_strdup (config_str); + if (0 != elen) + { + db->es = GNUNET_new_array (elen + 1, + struct GNUNET_PQ_ExecuteStatement); + memcpy (db->es, + es, + elen * sizeof (struct GNUNET_PQ_ExecuteStatement)); + } + if (0 != plen) + { + db->ps = GNUNET_new_array (plen + 1, + struct GNUNET_PQ_PreparedStatement); + memcpy (db->ps, + ps, + plen * sizeof (struct GNUNET_PQ_PreparedStatement)); + } + GNUNET_PQ_reconnect (db); + if (NULL == db->conn) + { + GNUNET_free (db->config_str); + GNUNET_free (db); + return NULL; + } + return db; +} + - conn = PQconnectdb (config_str); - if ((NULL == conn) || - (CONNECTION_OK != - PQstatus (conn))) +/** + * Reinitialize the database @a db. + * + * @param db database connection to reinitialize + */ +void +GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db) +{ + if (NULL != db->conn) + PQfinish (db->conn); + db->conn = PQconnectdb (db->config_str); + if ((NULL == db->conn) || + (CONNECTION_OK != PQstatus (db->conn))) { GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "pq", "Database connection to '%s' failed: %s\n", - config_str, - (NULL != conn) ? - PQerrorMessage (conn) + db->config_str, + (NULL != db->conn) ? + PQerrorMessage (db->conn) : "PQconnectdb returned NULL"); - if (NULL != conn) - PQfinish (conn); - return NULL; + if (NULL != db->conn) + { + PQfinish (db->conn); + db->conn = NULL; + } + return; } - PQsetNoticeReceiver (conn, + PQsetNoticeReceiver (db->conn, &pq_notice_receiver_cb, - conn); - PQsetNoticeProcessor (conn, + db); + PQsetNoticeProcessor (db->conn, &pq_notice_processor_cb, - conn); - return conn; + db); + if ( (NULL != db->es) && + (GNUNET_OK != + GNUNET_PQ_exec_statements (db, + db->es)) ) + { + PQfinish (db->conn); + db->conn = NULL; + return; + } + if ( (NULL != db->ps) && + (GNUNET_OK != + GNUNET_PQ_prepare_statements (db, + db->ps)) ) + { + PQfinish (db->conn); + db->conn = NULL; + return; + } } /** * Connect to a postgres database using the configuration - * option "CONFIG" in @a section. + * option "CONFIG" in @a section. Also ensures that the + * statements in @a es are executed whenever we (re)connect to the + * database, and that the prepared statements in @a ps are "ready". + * + * The caller MUST ensure that @a es and @a ps remain allocated and + * initialized in memory until #GNUNET_PQ_disconnect() is called, + * as they may be needed repeatedly and no copy will be made. * * @param cfg configuration * @param section configuration section to use to get Postgres configuration options + * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated + * array of statements to execute upon EACH connection, can be NULL + * @param ps array of prepared statements to prepare, can be NULL * @return the postgres handle, NULL on error */ -PGconn * +struct GNUNET_PQ_Context * GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *section) + const char *section, + const struct GNUNET_PQ_ExecuteStatement *es, + const struct GNUNET_PQ_PreparedStatement *ps) { - PGconn *dbh; + struct GNUNET_PQ_Context *db; 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); + db = GNUNET_PQ_connect (conninfo == NULL ? "" : conninfo, + es, + ps); GNUNET_free_non_null (conninfo); - return dbh; + return db; } +/** + * Disconnect from the database, destroying the prepared statements + * and releasing other associated resources. + * + * @param db database handle to disconnect (will be free'd) + */ +void +GNUNET_PQ_disconnect (struct GNUNET_PQ_Context *db) +{ + GNUNET_free_non_null (db->es); + GNUNET_free_non_null (db->ps); + PQfinish (db->conn); + GNUNET_free (db); +} + /* end of pq/pq_connect.c */ diff --git a/src/pq/pq_eval.c b/src/pq/pq_eval.c index 1d041f226..5bcf8ca0e 100644 --- a/src/pq/pq_eval.c +++ b/src/pq/pq_eval.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2017 GNUnet e.V. + Copyright (C) 2017, 2019 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -23,8 +23,7 @@ * @author Christian Grothoff */ #include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_pq_lib.h" +#include "pq.h" /** @@ -47,7 +46,7 @@ * Check the @a result's error code to see what happened. * Also logs errors. * - * @param connection connection to execute the statement in + * @param db database to execute the statement with * @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 @@ -57,17 +56,31 @@ * @deprecated (low level, let's see if we can do with just the high-level functions) */ enum GNUNET_DB_QueryStatus -GNUNET_PQ_eval_result (PGconn *connection, +GNUNET_PQ_eval_result (struct GNUNET_PQ_Context *db, const char *statement_name, PGresult *result) { ExecStatusType est; + if (NULL == result) + return GNUNET_DB_STATUS_SOFT_ERROR; est = PQresultStatus (result); if ((PGRES_COMMAND_OK != est) && (PGRES_TUPLES_OK != est)) { const char *sqlstate; + ConnStatusType status; + + if (CONNECTION_OK != (status = PQstatus (db->conn))) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, + "pq", + "Database connection failed during query `%s': %d (reconnecting)\n", + statement_name, + status); + GNUNET_PQ_reconnect (db); + return GNUNET_DB_STATUS_SOFT_ERROR; + } sqlstate = PQresultErrorField (result, PG_DIAG_SQLSTATE); @@ -94,7 +107,7 @@ GNUNET_PQ_eval_result (PGconn *connection, PG_DIAG_MESSAGE_DETAIL), PQresultErrorMessage (result), PQresStatus (PQresultStatus (result)), - PQerrorMessage (connection)); + PQerrorMessage (db->conn)); return GNUNET_DB_STATUS_SOFT_ERROR; } if (0 == strcmp (sqlstate, @@ -111,7 +124,7 @@ GNUNET_PQ_eval_result (PGconn *connection, PG_DIAG_MESSAGE_DETAIL), PQresultErrorMessage (result), PQresStatus (PQresultStatus (result)), - PQerrorMessage (connection)); + PQerrorMessage (db->conn)); return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; } GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, @@ -124,7 +137,7 @@ GNUNET_PQ_eval_result (PGconn *connection, PG_DIAG_MESSAGE_DETAIL), PQresultErrorMessage (result), PQresStatus (PQresultStatus (result)), - PQerrorMessage (connection)); + PQerrorMessage (db->conn)); return GNUNET_DB_STATUS_HARD_ERROR; } return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; @@ -136,7 +149,7 @@ GNUNET_PQ_eval_result (PGconn *connection, * statement in @a connnection using the given @a params. Returns the * resulting session state. * - * @param connection connection to execute the statement in + * @param db database to execute the statement with * @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 @@ -148,17 +161,19 @@ GNUNET_PQ_eval_result (PGconn *connection, * zero; if INSERT was successful, we return one. */ enum GNUNET_DB_QueryStatus -GNUNET_PQ_eval_prepared_non_select (PGconn *connection, +GNUNET_PQ_eval_prepared_non_select (struct GNUNET_PQ_Context *db, const char *statement_name, const struct GNUNET_PQ_QueryParam *params) { PGresult *result; enum GNUNET_DB_QueryStatus qs; - result = GNUNET_PQ_exec_prepared (connection, + result = GNUNET_PQ_exec_prepared (db, statement_name, params); - qs = GNUNET_PQ_eval_result (connection, + if (NULL == result) + return GNUNET_DB_STATUS_SOFT_ERROR; + qs = GNUNET_PQ_eval_result (db, statement_name, result); if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) @@ -182,7 +197,7 @@ GNUNET_PQ_eval_prepared_non_select (PGconn *connection, * 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 db database to execute the statement with * @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 @@ -191,7 +206,7 @@ GNUNET_PQ_eval_prepared_non_select (PGconn *connection, * codes to `enum GNUNET_DB_QueryStatus`. */ enum GNUNET_DB_QueryStatus -GNUNET_PQ_eval_prepared_multi_select (PGconn *connection, +GNUNET_PQ_eval_prepared_multi_select (struct GNUNET_PQ_Context *db, const char *statement_name, const struct GNUNET_PQ_QueryParam *params, GNUNET_PQ_PostgresResultHandler rh, @@ -201,10 +216,12 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection, enum GNUNET_DB_QueryStatus qs; unsigned int ret; - result = GNUNET_PQ_exec_prepared (connection, + result = GNUNET_PQ_exec_prepared (db, statement_name, params); - qs = GNUNET_PQ_eval_result (connection, + if (NULL == result) + return GNUNET_DB_STATUS_SOFT_ERROR; + qs = GNUNET_PQ_eval_result (db, statement_name, result); if (qs < 0) @@ -230,7 +247,7 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection, * value was #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT. Returns the * resulting session status. * - * @param connection connection to execute the statement in + * @param db database to execute the statement with * @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 @@ -238,7 +255,7 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection, * codes to `enum GNUNET_DB_QueryStatus`. */ enum GNUNET_DB_QueryStatus -GNUNET_PQ_eval_prepared_singleton_select (PGconn *connection, +GNUNET_PQ_eval_prepared_singleton_select (struct GNUNET_PQ_Context *db, const char *statement_name, const struct GNUNET_PQ_QueryParam *params, @@ -247,10 +264,12 @@ GNUNET_PQ_eval_prepared_singleton_select (PGconn *connection, PGresult *result; enum GNUNET_DB_QueryStatus qs; - result = GNUNET_PQ_exec_prepared (connection, + result = GNUNET_PQ_exec_prepared (db, statement_name, params); - qs = GNUNET_PQ_eval_result (connection, + if (NULL == result) + return GNUNET_DB_STATUS_SOFT_ERROR; + qs = GNUNET_PQ_eval_result (db, statement_name, result); if (qs < 0) diff --git a/src/pq/pq_exec.c b/src/pq/pq_exec.c index 00527151a..fd4feae53 100644 --- a/src/pq/pq_exec.c +++ b/src/pq/pq_exec.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2017 GNUnet e.V. + Copyright (C) 2017, 2019 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -23,8 +23,7 @@ * @author Christian Grothoff */ #include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_pq_lib.h" +#include "pq.h" /** @@ -67,14 +66,14 @@ 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 db database to execute the statements with * @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, +GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db, const struct GNUNET_PQ_ExecuteStatement *es) { for (unsigned int i = 0; NULL != es[i].sql; i++) @@ -84,8 +83,8 @@ GNUNET_PQ_exec_statements (PGconn *connection, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running statement `%s' on %p\n", es[i].sql, - connection); - result = PQexec (connection, + db); + result = PQexec (db->conn, es[i].sql); if ((GNUNET_NO == es[i].ignore_errors) && (PGRES_COMMAND_OK != PQresultStatus (result))) @@ -100,7 +99,7 @@ GNUNET_PQ_exec_statements (PGconn *connection, PG_DIAG_MESSAGE_DETAIL), PQresultErrorMessage (result), PQresStatus (PQresultStatus (result)), - PQerrorMessage (connection)); + PQerrorMessage (db->conn)); PQclear (result); return GNUNET_SYSERR; } diff --git a/src/pq/pq_prepare.c b/src/pq/pq_prepare.c index 0facf100f..b7003fb69 100644 --- a/src/pq/pq_prepare.c +++ b/src/pq/pq_prepare.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2017 GNUnet e.V. + Copyright (C) 2017, 2019 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -23,8 +23,7 @@ * @author Christian Grothoff */ #include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_pq_lib.h" +#include "pq.h" /** @@ -53,16 +52,42 @@ GNUNET_PQ_make_prepare (const char *name, /** * Request creation of prepared statements @a ps from Postgres. * - * @param connection connection to prepare the statements for + * @param db database 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, +GNUNET_PQ_prepare_statements (struct GNUNET_PQ_Context *db, const struct GNUNET_PQ_PreparedStatement *ps) { + if (db->ps != ps) + { + /* add 'ps' to list db->ps of prepared statements to run on reconnect! */ + unsigned int olen = 0; /* length of existing 'db->ps' array */ + unsigned int nlen = 0; /* length of 'ps' array */ + struct GNUNET_PQ_PreparedStatement *rps; /* combined array */ + + if (NULL != db->ps) + while (NULL != db->ps[olen].name) + olen++; + while (NULL != ps[nlen].name) + nlen++; + rps = GNUNET_new_array (olen + nlen + 1, + struct GNUNET_PQ_PreparedStatement); + if (NULL != db->ps) + memcpy (rps, + db->ps, + olen * sizeof (struct GNUNET_PQ_PreparedStatement)); + memcpy (&rps[olen], + ps, + nlen * sizeof (struct GNUNET_PQ_PreparedStatement)); + GNUNET_free_non_null (db->ps); + db->ps = rps; + } + + /* actually prepare statements */ for (unsigned int i = 0; NULL != ps[i].name; i++) { PGresult *ret; @@ -72,7 +97,7 @@ GNUNET_PQ_prepare_statements (PGconn *connection, "Preparing SQL statement `%s' as `%s'\n", ps[i].sql, ps[i].name); - ret = PQprepare (connection, + ret = PQprepare (db->conn, ps[i].name, ps[i].sql, ps[i].num_arguments, @@ -84,7 +109,7 @@ GNUNET_PQ_prepare_statements (PGconn *connection, _ ("PQprepare (`%s' as `%s') failed with error: %s\n"), ps[i].sql, ps[i].name, - PQerrorMessage (connection)); + PQerrorMessage (db->conn)); PQclear (ret); return GNUNET_SYSERR; } diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c index cfb16ac12..1fb1e71c0 100644 --- a/src/pq/pq_result_helper.c +++ b/src/pq/pq_result_helper.c @@ -587,7 +587,7 @@ extract_abs_time (void *cls, res = (int64_t *) PQgetvalue (result, row, fnum); - if (INT64_MAX == *res) + if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res)) *udst = GNUNET_TIME_UNIT_FOREVER_ABS; else udst->abs_value_us = GNUNET_ntohll ((uint64_t) *res); diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c index 697d8e580..a103aca5d 100644 --- a/src/pq/test_pq.c +++ b/src/pq/test_pq.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - (C) 2015, 2016 GNUnet e.V. + (C) 2015, 2016, 2019 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -23,75 +23,65 @@ * @author Christian Grothoff */ #include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_pq_lib.h" +#include "pq.h" /** * Setup prepared statements. * - * @param db_conn connection handle to initialize + * @param db database handle to initialize * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ static int -postgres_prepare (PGconn *db_conn) +postgres_prepare (struct GNUNET_PQ_Context *db) { - PGresult *result; - -#define PREPARE(name, sql, ...) \ - do { \ - result = PQprepare (db_conn, name, sql, __VA_ARGS__); \ - if (PGRES_COMMAND_OK != PQresultStatus (result)) \ - { \ - GNUNET_break (0); \ - PQclear (result); result = NULL; \ - return GNUNET_SYSERR; \ - } \ - PQclear (result); result = NULL; \ - } while (0); + struct GNUNET_PQ_PreparedStatement ps[] = { + GNUNET_PQ_make_prepare ("test_insert", + "INSERT INTO test_pq (" + " pub" + ",sig" + ",abs_time" + ",forever" + ",hash" + ",vsize" + ",u16" + ",u32" + ",u64" + ") VALUES " + "($1, $2, $3, $4, $5, $6," + "$7, $8, $9);", + 9), + GNUNET_PQ_make_prepare ("test_select", + "SELECT" + " pub" + ",sig" + ",abs_time" + ",forever" + ",hash" + ",vsize" + ",u16" + ",u32" + ",u64" + " FROM test_pq" + " ORDER BY abs_time DESC " + " LIMIT 1;", + 0), + GNUNET_PQ_PREPARED_STATEMENT_END + }; - PREPARE ("test_insert", - "INSERT INTO test_pq (" - " pub" - ",sig" - ",abs_time" - ",forever" - ",hash" - ",vsize" - ",u16" - ",u32" - ",u64" - ") VALUES " - "($1, $2, $3, $4, $5, $6," - "$7, $8, $9);", - 9, NULL); - PREPARE ("test_select", - "SELECT" - " pub" - ",sig" - ",abs_time" - ",forever" - ",hash" - ",vsize" - ",u16" - ",u32" - ",u64" - " FROM test_pq" - " ORDER BY abs_time DESC " - " LIMIT 1;", - 0, NULL); - return GNUNET_OK; -#undef PREPARE + return GNUNET_PQ_prepare_statements (db, + ps); } /** * Run actual test queries. * + * @param db database handle * @return 0 on success */ static int -run_queries (PGconn *conn) +run_queries (struct GNUNET_PQ_Context *db) { struct GNUNET_CRYPTO_RsaPublicKey *pub; struct GNUNET_CRYPTO_RsaPublicKey *pub2 = NULL; @@ -155,7 +145,7 @@ run_queries (PGconn *conn) GNUNET_PQ_result_spec_end }; - result = GNUNET_PQ_exec_prepared (conn, + result = GNUNET_PQ_exec_prepared (db, "test_insert", params_insert); if (PGRES_COMMAND_OK != PQresultStatus (result)) @@ -171,7 +161,7 @@ run_queries (PGconn *conn) } PQclear (result); - result = GNUNET_PQ_exec_prepared (conn, + result = GNUNET_PQ_exec_prepared (db, "test_select", params_select); if (1 != @@ -224,67 +214,71 @@ int main (int argc, const char *const argv[]) { - PGconn *conn; - PGresult *result; + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS test_pq (" + " pub BYTEA NOT NULL" + ",sig BYTEA NOT NULL" + ",abs_time INT8 NOT NULL" + ",forever INT8 NOT NULL" + ",hash BYTEA NOT NULL CHECK(LENGTH(hash)=64)" + ",vsize VARCHAR NOT NULL" + ",u16 INT2 NOT NULL" + ",u32 INT4 NOT NULL" + ",u64 INT8 NOT NULL" + ")"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + struct GNUNET_PQ_Context *db; int ret; GNUNET_log_setup ("test-pq", "WARNING", NULL); - conn = PQconnectdb ("postgres:///gnunetcheck"); - if (CONNECTION_OK != PQstatus (conn)) + db = GNUNET_PQ_connect ("postgres:///gnunetcheck", + es, + NULL); + if (CONNECTION_OK != PQstatus (db->conn)) { fprintf (stderr, "Cannot run test, database connection failed: %s\n", - PQerrorMessage (conn)); + PQerrorMessage (db->conn)); GNUNET_break (0); - PQfinish (conn); + GNUNET_PQ_disconnect (db); return 77; /* signal test was skipped */ } - - result = PQexec (conn, - "CREATE TEMPORARY TABLE IF NOT EXISTS test_pq (" - " pub BYTEA NOT NULL" - ",sig BYTEA NOT NULL" - ",abs_time INT8 NOT NULL" - ",forever INT8 NOT NULL" - ",hash BYTEA NOT NULL CHECK(LENGTH(hash)=64)" - ",vsize VARCHAR NOT NULL" - ",u16 INT2 NOT NULL" - ",u32 INT4 NOT NULL" - ",u64 INT8 NOT NULL" - ")"); - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - fprintf (stderr, - "Failed to create table: %s\n", - PQerrorMessage (conn)); - PQclear (result); - PQfinish (conn); - return 1; - } - PQclear (result); if (GNUNET_OK != - postgres_prepare (conn)) + postgres_prepare (db)) { GNUNET_break (0); - PQfinish (conn); + GNUNET_PQ_disconnect (db); return 1; } - ret = run_queries (conn); - result = PQexec (conn, - "DROP TABLE test_pq"); - if (PGRES_COMMAND_OK != PQresultStatus (result)) + ret = run_queries (db); +#if TEST_RESTART + fprintf (stderr, "Please restart Postgres database now!\n"); + sleep (60); + ret = run_queries (db); + fprintf (stderr, "Result: %d (expect: 1 -- if you restarted the DB)\n", ret); + ret = run_queries (db); + fprintf (stderr, "Result: %d (expect: 0)\n", ret); +#endif { - fprintf (stderr, - "Failed to create table: %s\n", - PQerrorMessage (conn)); - PQclear (result); - PQfinish (conn); - return 1; + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("DROP TABLE test_pq"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + if (GNUNET_OK != + GNUNET_PQ_exec_statements (db, + es)) + { + fprintf (stderr, + "Failed to drop table\n"); + GNUNET_PQ_disconnect (db); + return 1; + } } - PQclear (result); - PQfinish (conn); + GNUNET_PQ_disconnect (db); return ret; } -- cgit v1.2.3