exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 84af6e30af6e0b7c40f773dc05054e63d6313eae
parent 58fa8d9b37b39b728ca0a766b87c9cfd0184f943
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 13 Jun 2026 21:00:17 +0200

refactor to use new more robust GNUNET_PQ_init() style to handle database connection drops better

Diffstat:
Msrc/auditor/report-lib.c | 6++----
Msrc/auditor/taler-auditor-dbinit.c | 12++++++------
Msrc/auditor/taler-auditor-httpd.c | 6++----
Msrc/auditor/taler-auditor-sync.c | 6++----
Msrc/auditordb/create_tables.c | 68++++++++++++++++++++++++++++++++------------------------------------
Msrc/auditordb/drop_tables.c | 20++++++--------------
Msrc/auditordb/gc.c | 29++++++++---------------------
Msrc/auditordb/pg.c | 70+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/auditordb/preflight.c | 41+++--------------------------------------
Msrc/auditordb/test_auditordb.c | 8++++----
Msrc/auditordb/test_auditordb_checkpoints.c | 8++++----
Msrc/benchmark/taler-aggregator-benchmark.c | 3+--
Msrc/exchange-tools/taler-exchange-dbinit.c | 61++++++++-----------------------------------------------------
Msrc/exchange/taler-exchange-aggregator.c | 3+--
Msrc/exchange/taler-exchange-closer.c | 3+--
Msrc/exchange/taler-exchange-drain.c | 3+--
Msrc/exchange/taler-exchange-expire.c | 3+--
Msrc/exchange/taler-exchange-httpd.c | 3+--
Msrc/exchange/taler-exchange-router.c | 3+--
Msrc/exchange/taler-exchange-sanctionscheck.c | 3+--
Msrc/exchange/taler-exchange-transfer.c | 3+--
Msrc/exchange/taler-exchange-wirewatch.c | 3+--
Msrc/exchangedb/bench_db.c | 27+++++++++++++++++----------
Msrc/exchangedb/create_tables.c | 78++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/exchangedb/drop_tables.c | 24++++--------------------
Msrc/exchangedb/enable_rules.c | 33+++++++++++++++++++++++----------
Msrc/exchangedb/gc.c | 40+++++++++++++---------------------------
Msrc/exchangedb/inject_auditor_triggers.c | 30++++++++++++------------------
Msrc/exchangedb/pg.c | 116++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/exchangedb/preflight.c | 62+++-----------------------------------------------------------
Msrc/include/auditor-database/create_tables.h | 9+++++----
Msrc/include/auditordb_lib.h | 6+-----
Msrc/include/exchangedb_lib.h | 6+-----
Msrc/testing/testing_api_cmd_insert_deposit.c | 3+--
34 files changed, 364 insertions(+), 435 deletions(-)

diff --git a/src/auditor/report-lib.c b/src/auditor/report-lib.c @@ -768,8 +768,7 @@ TALER_ARL_init (const struct GNUNET_CONFIGURATION_Handle *c) } } if (NULL == - (TALER_ARL_edb = TALER_EXCHANGEDB_connect (TALER_ARL_cfg, - false))) + (TALER_ARL_edb = TALER_EXCHANGEDB_connect (TALER_ARL_cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize exchange database plugin.\n"); @@ -777,8 +776,7 @@ TALER_ARL_init (const struct GNUNET_CONFIGURATION_Handle *c) return GNUNET_SYSERR; } if (NULL == - (TALER_ARL_adb = TALER_AUDITORDB_connect (TALER_ARL_cfg, - false))) + (TALER_ARL_adb = TALER_AUDITORDB_connect (TALER_ARL_cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize auditor database plugin.\n"); diff --git a/src/auditor/taler-auditor-dbinit.c b/src/auditor/taler-auditor-dbinit.c @@ -25,6 +25,7 @@ #include "auditordb_lib.h" #include "auditor-database/gc.h" #include "auditor-database/drop_tables.h" +#include "auditor-database/preflight.h" #include "auditor-database/create_tables.h" @@ -69,11 +70,10 @@ run (void *cls, (void) args; (void) cfgfile; if (NULL == - (pg = TALER_AUDITORDB_connect (cfg, - true))) + (pg = TALER_AUDITORDB_connect (cfg))) { fprintf (stderr, - "Failed to initialize database plugin.\n"); + "Failed to initialize database connection.\n"); global_ret = EXIT_NOTINSTALLED; return; } @@ -81,7 +81,7 @@ run (void *cls, { if (GNUNET_OK != TALER_AUDITORDB_drop_tables (pg, - GNUNET_YES)) + true)) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to reset database\n"); } @@ -89,12 +89,12 @@ run (void *cls, { if (GNUNET_OK != TALER_AUDITORDB_drop_tables (pg, - GNUNET_NO)) + false)) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to restart audits\n"); } if (GNUNET_OK != - TALER_AUDITORDB_create_tables (cfg, + TALER_AUDITORDB_create_tables (pg, false, 0)) { diff --git a/src/auditor/taler-auditor-httpd.c b/src/auditor/taler-auditor-httpd.c @@ -1128,16 +1128,14 @@ static enum GNUNET_GenericReturnValue auditor_serve_process_config (void) { if (NULL == - (TAH_apg = TALER_AUDITORDB_connect (cfg, - false))) + (TAH_apg = TALER_AUDITORDB_connect (cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize DB subsystem to interact with auditor database\n"); return GNUNET_SYSERR; } if (NULL == - (TAH_epg = TALER_EXCHANGEDB_connect (cfg, - false))) + (TAH_epg = TALER_EXCHANGEDB_connect (cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize DB subsystem to query exchange database\n"); diff --git a/src/auditor/taler-auditor-sync.c b/src/auditor/taler-auditor-sync.c @@ -717,15 +717,13 @@ static void setup (struct GNUNET_CONFIGURATION_Handle *src_cfg, struct GNUNET_CONFIGURATION_Handle *dst_cfg) { - src = TALER_EXCHANGEDB_connect (src_cfg, - false); + src = TALER_EXCHANGEDB_connect (src_cfg); if (NULL == src) { global_ret = EXIT_NOTINSTALLED; return; } - dst = TALER_EXCHANGEDB_connect (dst_cfg, - false); + dst = TALER_EXCHANGEDB_connect (dst_cfg); if (NULL == dst) { global_ret = EXIT_NOTINSTALLED; diff --git a/src/auditordb/create_tables.c b/src/auditordb/create_tables.c @@ -25,11 +25,11 @@ enum GNUNET_GenericReturnValue TALER_AUDITORDB_create_tables ( - const struct GNUNET_CONFIGURATION_Handle *cfg, + struct TALER_AUDITORDB_PostgresContext *pg, bool support_partitions, uint32_t num_partitions) { - enum GNUNET_GenericReturnValue ret = GNUNET_OK; + bool ok = false; struct GNUNET_PQ_Context *conn; struct GNUNET_PQ_QueryParam params[] = { support_partitions @@ -37,46 +37,42 @@ TALER_AUDITORDB_create_tables ( : GNUNET_PQ_query_param_null (), GNUNET_PQ_query_param_end }; - struct GNUNET_PQ_PreparedStatement ps[] = { - GNUNET_PQ_make_prepare ("create_tables", - "SELECT" - " auditor.do_create_tables" - " ($1);"), - GNUNET_PQ_PREPARED_STATEMENT_END - }; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ("SET search_path TO auditor;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - conn = GNUNET_PQ_connect_with_cfg (cfg, - "auditordb-postgres", - "auditor-", - es, - ps); - if (NULL == conn) + if (GNUNET_SYSERR == + GNUNET_PQ_load_versioning (pg->conn)) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to connect to database\n"); + GNUNET_break (0); return GNUNET_SYSERR; } - if (0 > - GNUNET_PQ_eval_prepared_non_select (conn, - "create_tables", - params)) + if (GNUNET_OK != + GNUNET_PQ_run_sql (pg->conn, + "auditor-")) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_PQ_exec_sql (pg->conn, + "procedures")) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to run 'create_tables' prepared statement\n"); - ret = GNUNET_SYSERR; + GNUNET_break (0); + return GNUNET_SYSERR; } - if (GNUNET_OK == ret) + if (GNUNET_OK != + GNUNET_PQ_prepare_anon ( + pg->conn, + "SELECT auditor.do_create_tables($1);")) { - ret = GNUNET_PQ_exec_sql (conn, - "procedures"); - if (GNUNET_OK != ret) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to load stored procedures\n"); + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (0 > + GNUNET_PQ_eval_prepared_non_select (pg->conn, + "", + params)) + { + GNUNET_break (0); + return GNUNET_SYSERR; } - GNUNET_PQ_disconnect (conn); - return ret; + return GNUNET_OK; } diff --git a/src/auditordb/drop_tables.c b/src/auditordb/drop_tables.c @@ -22,23 +22,15 @@ #include "auditor-database/drop_tables.h" #include "pg_helper.h" + enum GNUNET_GenericReturnValue TALER_AUDITORDB_drop_tables (void *cls, bool drop_exchangelist) { - struct TALER_AUDITORDB_PostgresContext *pc = cls; - struct GNUNET_PQ_Context *conn; - enum GNUNET_GenericReturnValue ret; + struct TALER_AUDITORDB_PostgresContext *pg = cls; - conn = GNUNET_PQ_connect_with_cfg (pc->cfg, - "auditordb-postgres", - NULL, - NULL, - NULL); - if (NULL == conn) - return GNUNET_SYSERR; - ret = GNUNET_PQ_exec_sql (conn, - (drop_exchangelist) ? "drop" : "restart"); - GNUNET_PQ_disconnect (conn); - return ret; + return GNUNET_PQ_exec_sql (pg->conn, + (drop_exchangelist) + ? "drop" + : "restart"); } diff --git a/src/auditordb/gc.c b/src/auditordb/gc.c @@ -39,31 +39,18 @@ TALER_AUDITORDB_gc (struct TALER_AUDITORDB_PostgresContext *pg) GNUNET_PQ_query_param_absolute_time (&cutoff), GNUNET_PQ_query_param_end }; - struct GNUNET_PQ_Context *conn; enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_PreparedStatement ps[] = { - GNUNET_PQ_make_prepare ("do_gc_auditor", - "CALL" - " auditor.auditor_do_gc_auditor" - " ($1);"), - GNUNET_PQ_PREPARED_STATEMENT_END - }; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ("SET search_path TO auditor;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - conn = GNUNET_PQ_connect_with_cfg (pg->cfg, - "auditordb-postgres", - NULL, - es, - ps); - if (NULL == conn) + if (GNUNET_OK != + GNUNET_PQ_prepare_anon (pg->conn, + "CALL auditor_do_gc_auditor($1);")) + { + GNUNET_break (0); return GNUNET_SYSERR; - qs = GNUNET_PQ_eval_prepared_non_select (conn, - "do_gc_auditor", + } + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "", params_time); - GNUNET_PQ_disconnect (conn); if (0 > qs) { GNUNET_break (0); diff --git a/src/auditordb/pg.c b/src/auditordb/pg.c @@ -19,16 +19,74 @@ * @author Christian Grothoff * @author Gabor X Toth */ +struct TALER_AUDITORDB_PostgresContext; +#define GNUNET_PQ_RECONNECT_CALLBACK_CLOSURE \ + struct TALER_AUDITORDB_PostgresContext #include "taler/taler_pq_lib.h" #include <pthread.h> -#include <libpq-fe.h> #include "auditordb_lib.h" #include "pg_helper.h" +/** + * Function called each time we connect or reconnect to the + * database. Gives the application a chance to run some + * per-connection initialization logic. + * + * @param pg database conntext in the auditor + * @param pq database connection handle + */ +static void +reconnect_cb (struct TALER_AUDITORDB_PostgresContext *pg, + struct GNUNET_PQ_Context *pq) +{ + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_try_execute ("SET search_path TO auditor;"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + if (GNUNET_OK != + GNUNET_PQ_exec_statements (pq, + es)) + { + GNUNET_break (0); + return; + } + pg->prep_gen++; +} + + +/** + * Connect to the db if the connection does not exist yet. + * + * @param[in,out] pg the database state + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +setup_connection (struct TALER_AUDITORDB_PostgresContext *pg) +{ + struct GNUNET_PQ_Context *db_conn; + + if (NULL != pg->conn) + return GNUNET_OK; + db_conn = GNUNET_PQ_init (pg->cfg, + "auditordb-postgres", + &reconnect_cb, + pg); + if (NULL == db_conn) + return GNUNET_SYSERR; + if (0 == pg->prep_gen) + { + GNUNET_PQ_disconnect (db_conn); + return GNUNET_SYSERR; + } + pg->conn = db_conn; + return GNUNET_OK; +} + + struct TALER_AUDITORDB_PostgresContext * -TALER_AUDITORDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - bool skip_preflight) +TALER_AUDITORDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) { struct TALER_AUDITORDB_PostgresContext *pg; @@ -42,6 +100,12 @@ TALER_AUDITORDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_free (pg); return NULL; } + if (GNUNET_OK != + setup_connection (pg)) + { + TALER_AUDITORDB_disconnect (pg); + return NULL; + } return pg; } diff --git a/src/auditordb/preflight.c b/src/auditordb/preflight.c @@ -23,40 +23,6 @@ #include "pg_helper.h" -/** - * Connect to the db if the connection does not exist yet. - * - * @param[in,out] pg the plugin-specific state - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -setup_connection (struct TALER_AUDITORDB_PostgresContext *pg) -{ - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ("SET search_path TO auditor;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - struct GNUNET_PQ_Context *db_conn; - - if (NULL != pg->conn) - { - GNUNET_PQ_reconnect_if_down (pg->conn); - return GNUNET_OK; - } - db_conn = GNUNET_PQ_connect_with_cfg2 (pg->cfg, - "auditordb-postgres", - "auditor-", - es, - NULL, /* prepared statements */ - GNUNET_PQ_FLAG_CHECK_CURRENT); - if (NULL == db_conn) - return GNUNET_SYSERR; - pg->conn = db_conn; - pg->prep_gen++; - return GNUNET_OK; -} - - enum GNUNET_GenericReturnValue TALER_AUDITORDB_preflight (struct TALER_AUDITORDB_PostgresContext *pg) { @@ -65,12 +31,11 @@ TALER_AUDITORDB_preflight (struct TALER_AUDITORDB_PostgresContext *pg) GNUNET_PQ_EXECUTE_STATEMENT_END }; - if ( (NULL == pg->conn) && - (GNUNET_OK != - setup_connection (pg)) ) - return GNUNET_SYSERR; if (NULL == pg->transaction_name) + { + GNUNET_PQ_reconnect_if_down (pg->conn); return GNUNET_OK; /* all good */ + } if (GNUNET_OK == GNUNET_PQ_exec_statements (pg->conn, es)) diff --git a/src/auditordb/test_auditordb.c b/src/auditordb/test_auditordb.c @@ -216,17 +216,17 @@ run (void *cls) "connecting to database\n"); if (NULL == - (pg = TALER_AUDITORDB_connect (cfg, - true))) + (pg = TALER_AUDITORDB_connect (cfg))) { result = 77; return; } - + GNUNET_assert (GNUNET_OK == + TALER_AUDITORDB_preflight (pg)); (void) TALER_AUDITORDB_drop_tables (pg, GNUNET_YES); if (GNUNET_OK != - TALER_AUDITORDB_create_tables (cfg, + TALER_AUDITORDB_create_tables (pg, false, 0)) { diff --git a/src/auditordb/test_auditordb_checkpoints.c b/src/auditordb/test_auditordb_checkpoints.c @@ -99,19 +99,19 @@ run (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connecting to database\n"); if (NULL == - (pg = TALER_AUDITORDB_connect (cfg, - true))) + (pg = TALER_AUDITORDB_connect (cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to connect to database\n"); result = 77; return; } - + GNUNET_assert (GNUNET_OK == + TALER_AUDITORDB_preflight (pg)); (void) TALER_AUDITORDB_drop_tables (pg, GNUNET_YES); if (GNUNET_OK != - TALER_AUDITORDB_create_tables (cfg, + TALER_AUDITORDB_create_tables (pg, false, 0)) { diff --git a/src/benchmark/taler-aggregator-benchmark.c b/src/benchmark/taler-aggregator-benchmark.c @@ -469,8 +469,7 @@ run (void *cls, global_ret = EXIT_NOTCONFIGURED; return; } - pg = TALER_EXCHANGEDB_connect (cfg, - false); + pg = TALER_EXCHANGEDB_connect (cfg); if (NULL == pg) { global_ret = EXIT_NOTCONFIGURED; diff --git a/src/exchange-tools/taler-exchange-dbinit.c b/src/exchange-tools/taler-exchange-dbinit.c @@ -21,6 +21,7 @@ */ #include "platform.h" #include <gnunet/gnunet_util_lib.h> +#define GNUNET_PQ_RECONNECT_CALLBACK_CLOSURE bool #include <gnunet/gnunet_pq_lib.h> #include "exchangedb_lib.h" @@ -78,6 +79,7 @@ static uint32_t num_partitions; */ static int force_create_partitions; + /** * Main function that will be run. * @@ -92,50 +94,19 @@ run (void *cls, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { - struct GNUNET_PQ_Context *conn; struct TALER_EXCHANGEDB_PostgresContext *pg; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; (void) cls; (void) args; (void) cfgfile; - - if (NULL == - (conn = GNUNET_PQ_connect_with_cfg (cfg, - "exchangedb-postgres", - "exchange-", - es, - NULL))) - { - fprintf (stderr, - "Failed to initialize database.\n"); - global_ret = EXIT_NOTINSTALLED; - return; - } - if (NULL == - (pg = TALER_EXCHANGEDB_connect (cfg, - true))) + pg = TALER_EXCHANGEDB_connect (cfg); + if (NULL == pg) { fprintf (stderr, "Failed to initialize database connection.\n"); global_ret = EXIT_NOTINSTALLED; return; } - if (GNUNET_OK != - GNUNET_PQ_exec_sql (conn, - "procedures")) - { - GNUNET_PQ_disconnect (conn); - fprintf (stderr, - "Failed to load stored procedures.\n"); - global_ret = EXIT_NOTINSTALLED; - return; - } - - GNUNET_PQ_disconnect (conn); if (reset_db) { if (GNUNET_OK != @@ -147,8 +118,8 @@ run (void *cls, } if (GNUNET_OK != TALER_EXCHANGEDB_create_tables (pg, - force_create_partitions || num_partitions - > 0, + force_create_partitions || + (num_partitions > 0), num_partitions)) { fprintf (stderr, @@ -158,14 +129,6 @@ run (void *cls, } if (gc_db || clear_shards) { - if (GNUNET_OK != - TALER_EXCHANGEDB_preflight (pg)) - { - fprintf (stderr, - "Failed to prepare database.\n"); - global_ret = EXIT_NOPERMISSION; - goto exit; - } if (clear_shards) { if (GNUNET_OK != @@ -177,7 +140,8 @@ run (void *cls, } if (gc_db) { - if (GNUNET_SYSERR == TALER_EXCHANGEDB_gc (pg)) + if (GNUNET_SYSERR == + TALER_EXCHANGEDB_gc (pg)) { fprintf (stderr, "Garbage collection failed!\n"); @@ -204,14 +168,6 @@ run (void *cls, global_ret = EXIT_INVALIDARGUMENT; goto exit; } - if (GNUNET_OK != - TALER_EXCHANGEDB_preflight (pg)) - { - fprintf (stderr, - "Preflight check failed!\n"); - global_ret = EXIT_FAILURE; - goto exit; - } switch (TALER_EXCHANGEDB_disable_rules (pg, disable_rules)) { @@ -257,7 +213,6 @@ run (void *cls, } exit: TALER_EXCHANGEDB_disconnect (pg); - pg = NULL; } diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c @@ -522,8 +522,7 @@ parse_aggregator_config (void) } if (NULL == - (pg = TALER_EXCHANGEDB_connect (cfg, - false))) + (pg = TALER_EXCHANGEDB_connect (cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize DB subsystem\n"); diff --git a/src/exchange/taler-exchange-closer.c b/src/exchange/taler-exchange-closer.c @@ -159,8 +159,7 @@ parse_closer_config (void) } if (NULL == - (pg = TALER_EXCHANGEDB_connect (cfg, - false))) + (pg = TALER_EXCHANGEDB_connect (cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize DB subsystem\n"); diff --git a/src/exchange/taler-exchange-drain.c b/src/exchange/taler-exchange-drain.c @@ -140,8 +140,7 @@ parse_drain_config (void) GNUNET_free (master_public_key_str); } if (NULL == - (pg = TALER_EXCHANGEDB_connect (cfg, - false))) + (pg = TALER_EXCHANGEDB_connect (cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize DB subsystem\n"); diff --git a/src/exchange/taler-exchange-expire.c b/src/exchange/taler-exchange-expire.c @@ -145,8 +145,7 @@ static enum GNUNET_GenericReturnValue parse_expire_config (void) { if (NULL == - (pg = TALER_EXCHANGEDB_connect (cfg, - false))) + (pg = TALER_EXCHANGEDB_connect (cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize DB subsystem\n"); diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c @@ -2457,8 +2457,7 @@ exchange_serve_process_config (const char *cfg_fn) for (unsigned int i = 0; i<MAX_DB_RETRIES; i++) { - TEH_pg = TALER_EXCHANGEDB_connect (TEH_cfg, - false); + TEH_pg = TALER_EXCHANGEDB_connect (TEH_cfg); if (NULL != TEH_pg) break; GNUNET_log (GNUNET_ERROR_TYPE_WARNING, diff --git a/src/exchange/taler-exchange-router.c b/src/exchange/taler-exchange-router.c @@ -203,8 +203,7 @@ parse_wirewatch_config (void) } if (NULL == - (pg = TALER_EXCHANGEDB_connect (cfg, - false))) + (pg = TALER_EXCHANGEDB_connect (cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize DB subsystem\n"); diff --git a/src/exchange/taler-exchange-sanctionscheck.c b/src/exchange/taler-exchange-sanctionscheck.c @@ -696,8 +696,7 @@ run (void *cls, GNUNET_free (attr_enc_key_str); } if (NULL == - (pg = TALER_EXCHANGEDB_connect (cfg, - false))) + (pg = TALER_EXCHANGEDB_connect (cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize DB subsystem\n"); diff --git a/src/exchange/taler-exchange-transfer.c b/src/exchange/taler-exchange-transfer.c @@ -296,8 +296,7 @@ parse_transfer_config (void) return GNUNET_SYSERR; } if (NULL == - (pg = TALER_EXCHANGEDB_connect (cfg, - false))) + (pg = TALER_EXCHANGEDB_connect (cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize DB subsystem\n"); diff --git a/src/exchange/taler-exchange-wirewatch.c b/src/exchange/taler-exchange-wirewatch.c @@ -366,8 +366,7 @@ exchange_serve_process_config (void) return GNUNET_SYSERR; } if (NULL == - (pg = TALER_EXCHANGEDB_connect (cfg, - false))) + (pg = TALER_EXCHANGEDB_connect (cfg))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to initialize DB subsystem\n"); diff --git a/src/exchangedb/bench_db.c b/src/exchangedb/bench_db.c @@ -306,22 +306,29 @@ run (void *cls) pid_t f; int status; - conn = GNUNET_PQ_connect_with_cfg (cfg, - "bench-db-postgres", - "benchmark-", - NULL, - NULL); + conn = GNUNET_PQ_init (cfg, + "bench-db-postgres", + NULL, + NULL); if (NULL == conn) { result = EXIT_FAILURE; GNUNET_break (0); return; } - conn2 = GNUNET_PQ_connect_with_cfg (cfg, - "bench-db-postgres", - NULL, - NULL, - NULL); + if (GNUNET_OK != + GNUNET_PQ_exec_sql (conn, + "benchmark-")) + { + result = EXIT_FAILURE; + GNUNET_break (0); + GNUNET_PQ_disconnect (conn); + return; + } + conn2 = GNUNET_PQ_init (cfg, + "bench-db-postgres", + NULL, + NULL); if (! prepare (conn)) { GNUNET_PQ_disconnect (conn); diff --git a/src/exchangedb/create_tables.c b/src/exchangedb/create_tables.c @@ -24,55 +24,53 @@ enum GNUNET_GenericReturnValue -TALER_EXCHANGEDB_create_tables (struct TALER_EXCHANGEDB_PostgresContext *pg, - bool support_partitions, - uint32_t num_partitions) +TALER_EXCHANGEDB_create_tables ( + struct TALER_EXCHANGEDB_PostgresContext *pg, + bool support_partitions, + uint32_t num_partitions) { - struct GNUNET_PQ_Context *conn; - enum GNUNET_GenericReturnValue ret; struct GNUNET_PQ_QueryParam params[] = { support_partitions ? GNUNET_PQ_query_param_uint32 (&num_partitions) : GNUNET_PQ_query_param_null (), GNUNET_PQ_query_param_end }; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - conn = GNUNET_PQ_connect_with_cfg (pg->cfg, - "exchangedb-postgres", - "exchange-", - es, - NULL); - if (NULL == conn) + if (GNUNET_SYSERR == + GNUNET_PQ_load_versioning (pg->conn)) + { + GNUNET_break (0); return GNUNET_SYSERR; - ret = GNUNET_PQ_exec_sql (conn, - "procedures"); - GNUNET_break (GNUNET_OK == ret); - if (GNUNET_OK == ret) + } + if (GNUNET_OK != + GNUNET_PQ_run_sql (pg->conn, + "exchange-")) { - struct GNUNET_PQ_Context *tconn; - - tconn = pg->conn; - pg->prep_gen++; - pg->conn = conn; - PREPARE (pg, - "create_tables", - "SELECT" - " exchange_do_create_tables" - " ($1::INTEGER);"); - pg->conn = tconn; - if (0 > - GNUNET_PQ_eval_prepared_non_select (conn, - "create_tables", - params)) - { - GNUNET_break (0); - ret = GNUNET_SYSERR; - } + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_PQ_exec_sql (pg->conn, + "procedures")) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_PQ_prepare_anon ( + pg->conn, + "SELECT exchange_do_create_tables($1::INTEGER);")) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (0 > + GNUNET_PQ_eval_prepared_non_select (pg->conn, + "", + params)) + { + GNUNET_break (0); + return GNUNET_SYSERR; } - GNUNET_PQ_disconnect (conn); - return ret; + return GNUNET_OK; } diff --git a/src/exchangedb/drop_tables.c b/src/exchangedb/drop_tables.c @@ -30,25 +30,9 @@ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure */ enum GNUNET_GenericReturnValue -TALER_EXCHANGEDB_drop_tables (struct TALER_EXCHANGEDB_PostgresContext *pg) +TALER_EXCHANGEDB_drop_tables ( + struct TALER_EXCHANGEDB_PostgresContext *pg) { - struct GNUNET_PQ_Context *conn; - enum GNUNET_GenericReturnValue ret; - - if (NULL != pg->conn) - { - GNUNET_PQ_disconnect (pg->conn); - pg->conn = NULL; - } - conn = GNUNET_PQ_connect_with_cfg (pg->cfg, - "exchangedb-postgres", - NULL, - NULL, - NULL); - if (NULL == conn) - return GNUNET_SYSERR; - ret = GNUNET_PQ_exec_sql (conn, - "drop"); - GNUNET_PQ_disconnect (conn); - return ret; + return GNUNET_PQ_exec_sql (pg->conn, + "drop"); } diff --git a/src/exchangedb/enable_rules.c b/src/exchangedb/enable_rules.c @@ -28,7 +28,6 @@ TALER_EXCHANGEDB_enable_rules ( struct TALER_EXCHANGEDB_PostgresContext *pg, const char *schema) { - struct GNUNET_PQ_Context *conn; enum GNUNET_GenericReturnValue ret; char *sp; @@ -42,26 +41,29 @@ TALER_EXCHANGEDB_enable_rules ( }; char *schemadash; + if (GNUNET_OK != + GNUNET_PQ_exec_statements (pg->conn, + es)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } GNUNET_asprintf (&schemadash, "%s-", schema); - conn = GNUNET_PQ_connect_with_cfg (pg->cfg, - "exchangedb-postgres", - schemadash, - es, - NULL); + ret = GNUNET_PQ_run_sql (pg->conn, + schemadash); GNUNET_free (schemadash); } GNUNET_free (sp); - if (NULL == conn) - return GNUNET_SYSERR; + if (GNUNET_OK == ret) { char *procfile; GNUNET_asprintf (&procfile, "%s-procedures", schema); - ret = GNUNET_PQ_exec_sql (conn, + ret = GNUNET_PQ_exec_sql (pg->conn, procfile); /* $SCHEMA-procedures MAY not exist, so only check for hard error */ GNUNET_break (GNUNET_SYSERR != ret); @@ -69,6 +71,17 @@ TALER_EXCHANGEDB_enable_rules ( ret = GNUNET_OK; GNUNET_free (procfile); } - GNUNET_PQ_disconnect (conn); + { + struct GNUNET_PQ_ExecuteStatement es_restore[] = { + GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + /* This MUST succeed, otherwise we'll have a bad search_path + and then anything could happen... */ + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_exec_statements (pg->conn, + es_restore)); + } return ret; } diff --git a/src/exchangedb/gc.c b/src/exchangedb/gc.c @@ -33,8 +33,6 @@ TALER_EXCHANGEDB_gc (struct TALER_EXCHANGEDB_PostgresContext *pg) GNUNET_PQ_query_param_absolute_time (&now), GNUNET_PQ_query_param_end }; - struct GNUNET_PQ_Context *conn; - enum GNUNET_GenericReturnValue ret; /* Keep wire fees for 10 years, that should always be enough _and_ they are tiny so it does not @@ -44,32 +42,20 @@ TALER_EXCHANGEDB_gc (struct TALER_EXCHANGEDB_PostgresContext *pg) GNUNET_TIME_relative_multiply ( GNUNET_TIME_UNIT_YEARS, 10)); + if (GNUNET_OK != + GNUNET_PQ_prepare_anon (gc->conn, + "CALL exchange_do_gc($1,$2);")) { - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - struct GNUNET_PQ_PreparedStatement ps[] = { - GNUNET_PQ_make_prepare ("run_gc", - "CALL" - " exchange_do_gc" - " ($1,$2);"), - GNUNET_PQ_PREPARED_STATEMENT_END - }; - - conn = GNUNET_PQ_connect_with_cfg (pg->cfg, - "exchangedb-postgres", - NULL, - es, - ps); + GNUNET_break (0); + return GNUNET_SYSERR; } - if (NULL == conn) + if (0 > + GNUNET_PQ_eval_prepared_non_select (gc->conn, + "", + params)) + { + GNUNET_break (0); return GNUNET_SYSERR; - ret = GNUNET_OK; - if (0 > GNUNET_PQ_eval_prepared_non_select (conn, - "run_gc", - params)) - ret = GNUNET_SYSERR; - GNUNET_PQ_disconnect (conn); - return ret; + } + return GNUNET_OK; } diff --git a/src/exchangedb/inject_auditor_triggers.c b/src/exchangedb/inject_auditor_triggers.c @@ -18,6 +18,8 @@ * @brief Implementation of the inject_auditor_triggers function for Postgres * @author Christian Grothoff */ +#include <stdbool.h> +#define GNUNET_PQ_RECONNECT_CALLBACK_CLOSURE bool #include "taler/taler_pq_lib.h" #include "exchange-database/gc.h" #include "helper.h" @@ -25,31 +27,23 @@ /** - * Function called to inject auditor triggers into the - * database, triggering the real-time auditor upon - * relevant INSERTs. + * Function called to inject auditor triggers into the database, triggering + * the real-time auditor upon relevant INSERTs. * * @param pg the database context * @return #GNUNET_OK on success, * #GNUNET_SYSERR on DB errors */ enum GNUNET_GenericReturnValue -TALER_EXCHANGEDB_inject_auditor_triggers (struct - TALER_EXCHANGEDB_PostgresContext *pg) +TALER_EXCHANGEDB_inject_auditor_triggers ( + struct TALER_EXCHANGEDB_PostgresContext *pg) { - struct GNUNET_PQ_Context *conn; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - conn = GNUNET_PQ_connect_with_cfg (pg->cfg, - "exchangedb-postgres", - "auditor-triggers-", - es, - NULL); - if (NULL == conn) + if (GNUNET_OK != + GNUNET_PQ_run_sql (pg->conn, + "auditor-triggers-")) + { + GNUNET_break (0); return GNUNET_SYSERR; - GNUNET_PQ_disconnect (conn); + } return GNUNET_OK; } diff --git a/src/exchangedb/pg.c b/src/exchangedb/pg.c @@ -26,6 +26,9 @@ #include <poll.h> #include <pthread.h> #include <libpq-fe.h> +struct TALER_EXCHANGEDB_PostgresContext; +#define GNUNET_PQ_RECONNECT_CALLBACK_CLOSURE \ + struct TALER_EXCHANGEDB_PostgresContext #include "helper.h" #include "exchangedb_lib.h" #include "exchange-database/preflight.h" @@ -39,26 +42,96 @@ /** - * Log a really unexpected PQ error with all the details we can get hold of. + * Function called each time we connect or reconnect to the + * database. Gives the application a chance to run some + * per-connection initialization logic. * - * @param result PQ result object of the PQ operation that failed - * @param conn SQL connection that was used + * @param pg database conntext in the exchange + * @param pq database connection handle */ -#define BREAK_DB_ERR(result,conn) do { \ - GNUNET_break (0); \ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \ - "Database failure: %s/%s/%s/%s/%s", \ - PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY), \ - PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL), \ - PQresultErrorMessage (result), \ - PQresStatus (PQresultStatus (result)), \ - PQerrorMessage (conn)); \ -} while (0) +static void +reconnect_cb (struct TALER_EXCHANGEDB_PostgresContext *pg, + struct GNUNET_PQ_Context *pq) +{ +#if AUTO_EXPLAIN + /* Enable verbose logging to see where queries do not + properly use indices */ + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"), + GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"), + GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"), + GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"), + /* https://wiki.postgresql.org/wiki/Serializable suggests to really + force the default to 'serializable' if SSI is to be used. */ + GNUNET_PQ_make_try_execute ( + "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), + GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), + GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), + GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"), + /* Mergejoin causes issues, see Postgres #18380 */ + GNUNET_PQ_make_try_execute ("SET enable_mergejoin=OFF;"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; +#else + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_try_execute ( + "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), + GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), + GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), + /* Mergejoin causes issues, see Postgres #18380 */ + GNUNET_PQ_make_try_execute ("SET enable_mergejoin=OFF;"), + GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; +#endif + + if (GNUNET_OK != + GNUNET_PQ_exec_statements (pq, + es)) + { + GNUNET_break (0); + return; + } + pg->prep_gen++; +} + + +/** + * Connect to the db if the connection does not exist yet. + * + * @param[in,out] pg the database state + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +internal_setup (struct TALER_EXCHANGEDB_PostgresContext *pg) +{ + struct GNUNET_PQ_Context *db_conn; + + if (NULL != pg->conn) + return GNUNET_OK; + db_conn = GNUNET_PQ_init (pg->cfg, + "exchangedb-postgres", + &reconnect_cb, + pg); + if (NULL == db_conn) + return GNUNET_SYSERR; + if (0 == pg->prep_gen) + { + GNUNET_PQ_disconnect (db_conn); + return GNUNET_SYSERR; + } + if (0 == pg->prep_gen) + { + GNUNET_PQ_disconnect (db_conn); + return GNUNET_SYSERR; + } + pg->conn = db_conn; + return GNUNET_OK; +} struct TALER_EXCHANGEDB_PostgresContext * -TALER_EXCHANGEDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - bool skip_preflight) +TALER_EXCHANGEDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) { struct TALER_EXCHANGEDB_PostgresContext *pg; unsigned long long dpl; @@ -153,20 +226,15 @@ TALER_EXCHANGEDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, { goto fail; } - if (! skip_preflight) + if (GNUNET_OK != + internal_setup (pg)) { - if (GNUNET_OK != - TALER_EXCHANGEDB_preflight (pg)) - { - goto fail; - } + goto fail; } return pg; fail: - GNUNET_free (pg->exchange_url); - GNUNET_free (pg->sql_dir); - GNUNET_free (pg->currency); + TALER_EXCHANGEDB_disconnect (pg); GNUNET_free (pg); return NULL; } diff --git a/src/exchangedb/preflight.c b/src/exchangedb/preflight.c @@ -23,62 +23,6 @@ #include "helper.h" -static enum GNUNET_GenericReturnValue -internal_setup (struct TALER_EXCHANGEDB_PostgresContext *pg) -{ - if (NULL == pg->conn) - { -#if AUTO_EXPLAIN - /* Enable verbose logging to see where queries do not - properly use indices */ - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"), - GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"), - GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"), - GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"), - /* https://wiki.postgresql.org/wiki/Serializable suggests to really - force the default to 'serializable' if SSI is to be used. */ - GNUNET_PQ_make_try_execute ( - "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), - GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), - GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), - GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"), - /* Mergejoin causes issues, see Postgres #18380 */ - GNUNET_PQ_make_try_execute ("SET enable_mergejoin=OFF;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; -#else - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ( - "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), - GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), - GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), - /* Mergejoin causes issues, see Postgres #18380 */ - GNUNET_PQ_make_try_execute ("SET enable_mergejoin=OFF;"), - GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; -#endif - struct GNUNET_PQ_Context *db_conn; - - db_conn = GNUNET_PQ_connect_with_cfg2 (pg->cfg, - "exchangedb-postgres", - "exchange-", /* load_path_suffix */ - es, - NULL /* prepared statements */, - GNUNET_PQ_FLAG_CHECK_CURRENT); - if (NULL == db_conn) - return GNUNET_SYSERR; - - pg->prep_gen++; - pg->conn = db_conn; - } - if (NULL == pg->transaction_name) - GNUNET_PQ_reconnect_if_down (pg->conn); - return GNUNET_OK; -} - - enum GNUNET_GenericReturnValue TALER_EXCHANGEDB_preflight (struct TALER_EXCHANGEDB_PostgresContext *pg) { @@ -87,11 +31,11 @@ TALER_EXCHANGEDB_preflight (struct TALER_EXCHANGEDB_PostgresContext *pg) GNUNET_PQ_EXECUTE_STATEMENT_END }; - if (GNUNET_OK != - internal_setup (pg)) - return GNUNET_SYSERR; if (NULL == pg->transaction_name) + { + GNUNET_PQ_reconnect_if_down (pg->conn); return GNUNET_OK; /* all good */ + } if (GNUNET_OK == GNUNET_PQ_exec_statements (pg->conn, es)) diff --git a/src/include/auditor-database/create_tables.h b/src/include/auditor-database/create_tables.h @@ -29,15 +29,16 @@ /** * Create the necessary tables if they are not present * - * @param cfg configuration to use + * @param pg database handle to use * @param support_partitions true to support partitioning * @param num_partitions number of partitions to use * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure */ enum GNUNET_GenericReturnValue -TALER_AUDITORDB_create_tables (const struct GNUNET_CONFIGURATION_Handle *cfg, - bool support_partitions, - uint32_t num_partitions); +TALER_AUDITORDB_create_tables ( + struct TALER_AUDITORDB_PostgresContext *pg, + bool support_partitions, + uint32_t num_partitions); #endif diff --git a/src/include/auditordb_lib.h b/src/include/auditordb_lib.h @@ -606,14 +606,10 @@ struct TALER_AUDITORDB_AmountArithmeticInconsistency * Initialize database connection. * * @param cfg configuration to use - * @param skip_preflight true if we should skip the usual - * preflight check which assures us that the DB is actually - * operational; only taler-auditor-dbinit should use true here. * @return NULL on failure */ struct TALER_AUDITORDB_PostgresContext * -TALER_AUDITORDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - bool skip_preflight); +TALER_AUDITORDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg); /** diff --git a/src/include/exchangedb_lib.h b/src/include/exchangedb_lib.h @@ -888,14 +888,10 @@ typedef void * Initialize the database connection. * * @param cfg configuration to use - * @param skip_preflight true if we should skip the usual - * preflight check which assures us that the DB is actually - * operational; only taler-exchange-dbinit should use true here. * @return NULL on failure */ struct TALER_EXCHANGEDB_PostgresContext * -TALER_EXCHANGEDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - bool skip_preflight); +TALER_EXCHANGEDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg); /** diff --git a/src/testing/testing_api_cmd_insert_deposit.c b/src/testing/testing_api_cmd_insert_deposit.c @@ -379,8 +379,7 @@ TALER_TESTING_cmd_insert_deposit ( } else { - ids->plugin = TALER_EXCHANGEDB_connect (db_cfg, - false); + ids->plugin = TALER_EXCHANGEDB_connect (db_cfg); pluginc = ids->plugin; db_cfgc = db_cfg; }