donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

commit cb75aba77326901fbd06365887ff53b2f37a3e66
parent ece7efaa0fcfb3e2015a402a3fe2275fbc1a587d
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 13 Jun 2026 21:00:08 +0200

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

Diffstat:
Msrc/donaudb/create_tables.c | 63++++++++++++++++++++++++++++-----------------------------------
Msrc/donaudb/drop_tables.c | 27++-------------------------
Msrc/donaudb/plugin_donaudb_postgres.c | 143++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/donaudb/preflight.c | 3---
4 files changed, 103 insertions(+), 133 deletions(-)

diff --git a/src/donaudb/create_tables.c b/src/donaudb/create_tables.c @@ -29,53 +29,46 @@ enum GNUNET_GenericReturnValue DONAUDB_create_tables (struct DONAUDB_PostgresContext *ctx) { - struct GNUNET_PQ_Context *conn; - enum GNUNET_GenericReturnValue ret = GNUNET_OK; - struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_end }; - struct GNUNET_PQ_PreparedStatement ps[] = { - GNUNET_PQ_make_prepare ("create_tables", - "CALL" - " donau.do_create_tables" - " ();"), - GNUNET_PQ_PREPARED_STATEMENT_END - }; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ("SET search_path TO donau;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - conn = GNUNET_PQ_connect_with_cfg (ctx->cfg, - "donaudb-postgres", - "donau-", - es, - ps); - if (NULL == conn) + if (GNUNET_SYSERR == + GNUNET_PQ_load_versioning (ctx->conn)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_PQ_run_sql (ctx->conn, + "donau-")) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_PQ_exec_sql (ctx->conn, + "procedures")) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to connect to database\n"); + "Failed to load stored procedures\n"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_PQ_prepare_anon (ctx->conn, + "CALL donau.do_create_tables();")) + { + GNUNET_break (0); return GNUNET_SYSERR; } if (0 > - GNUNET_PQ_eval_prepared_non_select (conn, - "create_tables", + GNUNET_PQ_eval_prepared_non_select (ctx->conn, + "", params)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create tables\n"); - ret = GNUNET_SYSERR; - } - if (GNUNET_OK == ret) - { - ret = GNUNET_PQ_exec_sql (conn, - "procedures"); - if (GNUNET_OK != ret) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to load stored procedures: %d\n", - ret); + return GNUNET_SYSERR; } - GNUNET_PQ_disconnect (conn); - return ret; + return GNUNET_OK; } diff --git a/src/donaudb/drop_tables.c b/src/donaudb/drop_tables.c @@ -26,32 +26,9 @@ #include "helper.h" -/** - * Drop all Taler tables. This should only be used by testcases. - * - * @param ctx the `struct DONAUDB_PostgresContext` with the plugin-specific state - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ enum GNUNET_GenericReturnValue DONAUDB_drop_tables (struct DONAUDB_PostgresContext *ctx) { - struct GNUNET_PQ_Context *conn; - enum GNUNET_GenericReturnValue ret; - - if (NULL != ctx->conn) - { - GNUNET_PQ_disconnect (ctx->conn); - ctx->conn = NULL; - } - conn = GNUNET_PQ_connect_with_cfg (ctx->cfg, - "donaudb-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 (ctx->conn, + "drop"); } diff --git a/src/donaudb/plugin_donaudb_postgres.c b/src/donaudb/plugin_donaudb_postgres.c @@ -23,6 +23,9 @@ #include <poll.h> #include <pthread.h> #include <libpq-fe.h> +struct DONAUDB_PostgresContext; +#define GNUNET_PQ_RECONNECT_CALLBACK_CLOSURE \ + struct DONAUDB_PostgresContext #include "helper.h" /** @@ -34,21 +37,55 @@ /** - * 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 donau + * @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 DONAUDB_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 donau;"), + 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;"), + GNUNET_PQ_make_try_execute ("SET autocommit=OFF;"), + GNUNET_PQ_make_try_execute ("SET search_path TO donau;"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; +#endif + + if (GNUNET_OK != + GNUNET_PQ_exec_statements (pq, + es)) + { + GNUNET_break (0); + return; + } + pg->prep_gen++; +} /** @@ -57,54 +94,25 @@ * @param pg the plugin-specific state * @return #GNUNET_OK on success */ -enum GNUNET_GenericReturnValue -DONAUDB_internal_setup (struct DONAUDB_PostgresContext *pg) +static enum GNUNET_GenericReturnValue +internal_setup (struct DONAUDB_PostgresContext *pg) { - if (NULL == pg->conn) + struct GNUNET_PQ_Context *db_conn; + + if (NULL != pg->conn) + return GNUNET_OK; + db_conn = GNUNET_PQ_init (pg->cfg, + "donaudb-postgres", + &reconnect_cb, + pg); + if (NULL == db_conn) + return GNUNET_SYSERR; + if (0 == pg->prep_gen) { -#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 donau;"), - 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;"), - GNUNET_PQ_make_try_execute ("SET autocommit=OFF;"), - GNUNET_PQ_make_try_execute ("SET search_path TO donau;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; -#endif - struct GNUNET_PQ_Context *db_conn; - - db_conn = GNUNET_PQ_connect_with_cfg (pg->cfg, - "donaudb-postgres", - NULL, - es, - NULL); - if (NULL == db_conn) - return GNUNET_SYSERR; - - pg->prep_gen++; - pg->conn = db_conn; + GNUNET_PQ_disconnect (db_conn); + return GNUNET_SYSERR; } - if (NULL == pg->transaction_name) - GNUNET_PQ_reconnect_if_down (pg->conn); + pg->conn = db_conn; return GNUNET_OK; } @@ -125,27 +133,22 @@ DONAUDB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "donau", "BASE_URL"); - GNUNET_free (pg); - return NULL; + goto fail; } if (GNUNET_OK != TALER_config_get_currency (cfg, "donau", &pg->currency)) { - GNUNET_free (pg->donau_url); - GNUNET_free (pg); - return NULL; + goto fail; } if (GNUNET_OK != - DONAUDB_internal_setup (pg)) - { - GNUNET_free (pg->donau_url); - GNUNET_free (pg->currency); - GNUNET_free (pg); - return NULL; - } + internal_setup (pg)) + goto fail; return pg; +fail: + DONAUDB_disconnect (pg); + return NULL; } diff --git a/src/donaudb/preflight.c b/src/donaudb/preflight.c @@ -34,9 +34,6 @@ DONAUDB_preflight (struct DONAUDB_PostgresContext *ctx) GNUNET_PQ_EXECUTE_STATEMENT_END }; - if (GNUNET_OK != - DONAUDB_internal_setup (ctx)) - return GNUNET_SYSERR; if (NULL == ctx->transaction_name) return GNUNET_OK; /* all good */ if (GNUNET_OK ==