libgnunetpq API change to fix #5733
authorChristian Grothoff <christian@grothoff.org>
Fri, 11 Oct 2019 18:55:59 +0000 (20:55 +0200)
committerChristian Grothoff <christian@grothoff.org>
Fri, 11 Oct 2019 21:36:09 +0000 (23:36 +0200)
14 files changed:
src/datacache/plugin_datacache_postgres.c
src/datastore/plugin_datastore_postgres.c
src/include/gnunet_pq_lib.h
src/namecache/plugin_namecache_postgres.c
src/namestore/plugin_namestore_postgres.c
src/pq/Makefile.am
src/pq/pq.c
src/pq/pq.h [new file with mode: 0644]
src/pq/pq_connect.c
src/pq/pq_eval.c
src/pq/pq_exec.c
src/pq/pq_prepare.c
src/pq/pq_result_helper.c
src/pq/test_pq.c

index 59dff9067f2e9fb7577c874942cb703f0e927688..c532550aed0834bc8d2fc7c56a58f95f3c9a741e 100644 (file)
@@ -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;
index 181cf8cf884ac439cb89cb369140754c04a7b873..0811edfd7130f6db8e5a28e376ca57ab7269970c 100644 (file)
@@ -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;
index 6c576c8ab2d544b8fbe8b27befb15af10c819189..a56df21fde7bc6f922cb34ce81bf9a21562536ca 100644 (file)
  * @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_ */
index e4b360ef28d2f202df29c89395cd2b862bbf1210..35bf5c2ff624edb40f5bf7aed2706ba1ac2e5ad7 100644 (file)
@@ -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;
 }
 
index 5148ca0f5574a75a4b00ef2e4b6f4dcaff87d374..23893538b09d9565a81fd910142c3d37e6508a54 100644 (file)
@@ -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;
 }
 
index 9270e6fe0f08a9542b65935724f916f62dc2b4e7..750a1d48dddf841c11094c77f924051f28e5dc43 100644 (file)
@@ -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 = \
index 7e97c8f723311bf36679f1d4a13431513ff7067d..d2b9a6174dec3c02e2c83815c08304e5008346ed 100644 (file)
@@ -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
  * @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 = &params[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 (file)
index 0000000..b30f4f0
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+
+     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
index 79b9d6107e8748236e2b4e0cbedd13c24dc33571..7599f4b152de6948936eced96bd18fd1b3e8c0f5 100644 (file)
@@ -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 */
index 1d041f2261059d44dcc18343a830ba5fc2d06102..5bcf8ca0e972dfab103c12a92b722d5213411833 100644 (file)
@@ -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
  * @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)
index 00527151a94096ed0e5d9be42505bf1a07da66de..fd4feae53b38b018541113d7d4aac36ad9738a25 100644 (file)
@@ -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;
     }
index 0facf100fb6b5b15630439a3a4a569f97df93a6e..b7003fb69449f6a9e75c00b93cce472963f4111f 100644 (file)
@@ -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;
     }
index cfb16ac12f62e36c480add0739b41f768ba106b3..1fb1e71c011f86e011a9157c6c0b539fabba458d 100644 (file)
@@ -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);
index 697d8e5809134afc59de60fc2974d932badd402c..a103aca5d913d13650abfa0551fc33dcc719f234 100644 (file)
@@ -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
  * @author Christian Grothoff <christian@grothoff.org>
  */
 #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;
 }