plugin_donaudb_postgres.c (8744B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 /** 18 * @file plugin_donaudb_postgres.c 19 * @brief Low-level (statement-level) Postgres database access for the donau 20 * @author Johannes Casaburi 21 */ 22 #include "donau_config.h" 23 #include <poll.h> 24 #include <pthread.h> 25 #include <libpq-fe.h> 26 #include <taler/taler_error_codes.h> 27 #include <taler/taler_dbevents.h> 28 #include <taler/taler_pq_lib.h> 29 #include <taler/taler_util.h> 30 #include <taler/taler_json_lib.h> 31 #include "donaudb_plugin.h" 32 #include "pg_helper.h" 33 #include "pg_preflight.h" 34 #include "pg_commit.h" 35 #include "pg_drop_tables.h" 36 #include "pg_start.h" 37 #include "pg_rollback.h" 38 #include "pg_create_tables.h" 39 #include "pg_event_listen.h" 40 #include "pg_event_listen_cancel.h" 41 #include "pg_event_notify.h" 42 #include "pg_insert_donation_unit.h" 43 #include "pg_iterate_donation_units.h" 44 #include "pg_insert_history_entry.h" 45 #include "pg_get_history.h" 46 #include "pg_insert_issued_receipt.h" 47 #include "pg_insert_submitted_receipts.h" 48 #include "pg_iterate_submitted_receipts.h" 49 #include "pg_insert_signing_key.h" 50 #include "pg_iterate_active_signing_keys.h" 51 #include "pg_lookup_signing_key.h" 52 #include "pg_lookup_charity.h" 53 #include "pg_lookup_donation_unit_amount.h" 54 #include "pg_lookup_issued_receipts.h" 55 #include "pg_get_charities.h" 56 #include "pg_insert_charity.h" 57 #include "pg_update_charity.h" 58 #include "pg_do_charity_delete.h" 59 60 /** 61 * Set to 1 to enable Postgres auto_explain module. This will 62 * slow down things a _lot_, but also provide extensive logging 63 * in the Postgres database logger for performance analysis. 64 */ 65 #define AUTO_EXPLAIN 0 66 67 68 /** 69 * Log a really unexpected PQ error with all the details we can get hold of. 70 * 71 * @param result PQ result object of the PQ operation that failed 72 * @param conn SQL connection that was used 73 */ 74 #define BREAK_DB_ERR(result,conn) do { \ 75 GNUNET_break (0); \ 76 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \ 77 "Database failure: %s/%s/%s/%s/%s", \ 78 PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY), \ 79 PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL), \ 80 PQresultErrorMessage (result), \ 81 PQresStatus (PQresultStatus (result)), \ 82 PQerrorMessage (conn)); \ 83 } while (0) 84 85 86 /** 87 * Connect to the database if the connection does not exist yet. 88 * 89 * @param pg the plugin-specific state 90 * @return #GNUNET_OK on success 91 */ 92 enum GNUNET_GenericReturnValue 93 DH_PG_internal_setup (struct PostgresClosure *pg) 94 { 95 if (NULL == pg->conn) 96 { 97 #if AUTO_EXPLAIN 98 /* Enable verbose logging to see where queries do not 99 properly use indices */ 100 struct GNUNET_PQ_ExecuteStatement es[] = { 101 GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"), 102 GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"), 103 GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"), 104 GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"), 105 /* https://wiki.postgresql.org/wiki/Serializable suggests to really 106 force the default to 'serializable' if SSI is to be used. */ 107 GNUNET_PQ_make_try_execute ( 108 "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), 109 GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), 110 GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), 111 GNUNET_PQ_make_try_execute ("SET search_path TO donau;"), 112 GNUNET_PQ_EXECUTE_STATEMENT_END 113 }; 114 #else 115 struct GNUNET_PQ_ExecuteStatement es[] = { 116 GNUNET_PQ_make_try_execute ( 117 "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), 118 GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), 119 GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), 120 GNUNET_PQ_make_try_execute ("SET autocommit=OFF;"), 121 GNUNET_PQ_make_try_execute ("SET search_path TO donau;"), 122 GNUNET_PQ_EXECUTE_STATEMENT_END 123 }; 124 #endif 125 struct GNUNET_PQ_Context *db_conn; 126 127 db_conn = GNUNET_PQ_connect_with_cfg (pg->cfg, 128 "donaudb-postgres", 129 NULL, 130 es, 131 NULL); 132 if (NULL == db_conn) 133 return GNUNET_SYSERR; 134 135 pg->prep_gen++; 136 pg->conn = db_conn; 137 } 138 if (NULL == pg->transaction_name) 139 GNUNET_PQ_reconnect_if_down (pg->conn); 140 return GNUNET_OK; 141 } 142 143 144 /** 145 * Initialize Postgres database subsystem. 146 * 147 * @param cls a configuration instance 148 * @return NULL on error, otherwise a `struct 149 * DONAUDB_Plugin` 150 */ 151 void * 152 libdonau_plugin_donaudb_postgres_init (void *cls); 153 154 void * 155 libdonau_plugin_donaudb_postgres_init (void *cls) 156 { 157 const struct GNUNET_CONFIGURATION_Handle *cfg = cls; 158 struct PostgresClosure *pg; 159 struct DONAUDB_Plugin *plugin; 160 161 pg = GNUNET_new (struct PostgresClosure); 162 pg->cfg = cfg; 163 if (GNUNET_OK != 164 GNUNET_CONFIGURATION_get_value_string (cfg, 165 "donau", 166 "BASE_URL", 167 &pg->donau_url)) 168 { 169 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 170 "donau", 171 "BASE_URL"); 172 GNUNET_free (pg); 173 return NULL; 174 } 175 if (GNUNET_OK != 176 TALER_config_get_currency (cfg, 177 "donau", 178 &pg->currency)) 179 { 180 GNUNET_free (pg->donau_url); 181 GNUNET_free (pg); 182 return NULL; 183 } 184 if (GNUNET_OK != 185 DH_PG_internal_setup (pg)) 186 { 187 GNUNET_free (pg->donau_url); 188 GNUNET_free (pg->currency); 189 GNUNET_free (pg); 190 return NULL; 191 } 192 plugin = GNUNET_new (struct DONAUDB_Plugin); 193 plugin->cls = pg; 194 plugin->drop_tables 195 = &DH_PG_drop_tables; 196 plugin->commit 197 = &DH_PG_commit; 198 plugin->preflight 199 = &DH_PG_preflight; 200 plugin->start 201 = &DH_PG_start; 202 plugin->rollback 203 = &DH_PG_rollback; 204 plugin->create_tables 205 = &DH_PG_create_tables; 206 plugin->event_listen 207 = &DH_PG_event_listen; 208 plugin->event_listen_cancel 209 = &DH_PG_event_listen_cancel; 210 plugin->event_notify 211 = &DH_PG_event_notify; 212 plugin->insert_donation_unit 213 = &DH_PG_insert_donation_unit; 214 plugin->iterate_donation_units 215 = &DH_PG_iterate_donation_units; 216 plugin->lookup_donation_unit_amount 217 = &DH_PG_lookup_donation_unit_amount; 218 plugin->insert_history_entry 219 = &DH_PG_insert_history_entry; 220 plugin->get_history 221 = &DH_PG_get_history; 222 plugin->insert_issued_receipt 223 = &DH_PG_insert_issued_receipt; 224 plugin->lookup_issued_receipts 225 = &DH_PG_lookup_issued_receipts; 226 plugin->insert_submitted_receipts 227 = &DH_PG_insert_submitted_receipts; 228 plugin->insert_signing_key 229 = &DH_PG_insert_signing_key; 230 plugin->lookup_signing_key 231 = &DH_PG_lookup_signing_key; 232 plugin->iterate_active_signing_keys 233 = &DH_PG_iterate_active_signing_keys; 234 plugin->iterate_submitted_receipts 235 = &DH_PG_iterate_submitted_receipts; 236 plugin->lookup_charity 237 = &DH_PG_lookup_charity; 238 plugin->insert_charity 239 = &DH_PG_insert_charity; 240 plugin->update_charity 241 = &DH_PG_update_charity; 242 plugin->get_charities 243 = &DH_PG_get_charities; 244 plugin->do_charity_delete 245 = &DH_PG_do_charity_delete; 246 247 return plugin; 248 } 249 250 251 /** 252 * Shutdown Postgres database subsystem. 253 * 254 * @param cls a `struct DONAUDB_Plugin` 255 * @return NULL (always) 256 */ 257 void * 258 libdonau_plugin_donaudb_postgres_done (void *cls); 259 260 void * 261 libdonau_plugin_donaudb_postgres_done (void *cls) 262 { 263 struct DONAUDB_Plugin *plugin = cls; 264 struct PostgresClosure *pg = plugin->cls; 265 266 if (NULL != pg->conn) 267 { 268 GNUNET_PQ_disconnect (pg->conn); 269 pg->conn = NULL; 270 } 271 GNUNET_free (pg->donau_url); 272 GNUNET_free (pg->currency); 273 GNUNET_free (pg); 274 GNUNET_free (plugin); 275 return NULL; 276 } 277 278 279 /* end of plugin_donaudb_postgres.c */