pg.c (9345B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014--2025 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 pg.c 19 * @brief Low-level (statement-level) Postgres database access for the exchange 20 * @author Florian Dold 21 * @author Christian Grothoff 22 * @author Sree Harsha Totakura 23 * @author Marcello Stanisci 24 * @author Özgür Kesim 25 */ 26 #include <poll.h> 27 #include <pthread.h> 28 #include <libpq-fe.h> 29 struct TALER_EXCHANGEDB_PostgresContext; 30 #define GNUNET_PQ_RECONNECT_CALLBACK_CLOSURE \ 31 struct TALER_EXCHANGEDB_PostgresContext 32 #include "helper.h" 33 #include "exchangedb_lib.h" 34 #include "exchange-database/preflight.h" 35 36 /** 37 * Set to 1 to enable Postgres auto_explain module. This will 38 * slow down things a _lot_, but also provide extensive logging 39 * in the Postgres database logger for performance analysis. 40 */ 41 #define AUTO_EXPLAIN 0 42 43 /** 44 * Counts how often we have established a fresh @e conn 45 * to the database. Used to re-prepare statements. 46 */ 47 unsigned long long TEH_PG_prep_gen_; 48 49 /** 50 * Function called each time we connect or reconnect to the 51 * database. Gives the application a chance to run some 52 * per-connection initialization logic. 53 * 54 * @param pg database conntext in the exchange 55 * @param pq database connection handle 56 */ 57 static void 58 reconnect_cb (struct TALER_EXCHANGEDB_PostgresContext *pg, 59 struct GNUNET_PQ_Context *pq) 60 { 61 #if AUTO_EXPLAIN 62 /* Enable verbose logging to see where queries do not 63 properly use indices */ 64 struct GNUNET_PQ_ExecuteStatement es[] = { 65 GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"), 66 GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"), 67 GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"), 68 GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"), 69 /* https://wiki.postgresql.org/wiki/Serializable suggests to really 70 force the default to 'serializable' if SSI is to be used. */ 71 GNUNET_PQ_make_try_execute ( 72 "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), 73 GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), 74 GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), 75 GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"), 76 /* Mergejoin causes issues, see Postgres #18380 */ 77 GNUNET_PQ_make_try_execute ("SET enable_mergejoin=OFF;"), 78 GNUNET_PQ_EXECUTE_STATEMENT_END 79 }; 80 #else 81 struct GNUNET_PQ_ExecuteStatement es[] = { 82 GNUNET_PQ_make_try_execute ( 83 "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), 84 GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), 85 GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), 86 /* Mergejoin causes issues, see Postgres #18380 */ 87 GNUNET_PQ_make_try_execute ("SET enable_mergejoin=OFF;"), 88 GNUNET_PQ_make_try_execute ("SET search_path TO exchange;"), 89 GNUNET_PQ_EXECUTE_STATEMENT_END 90 }; 91 #endif 92 93 if (GNUNET_OK != 94 GNUNET_PQ_exec_statements (pq, 95 es)) 96 { 97 GNUNET_break (0); 98 return; 99 } 100 TEH_PG_prep_gen_++; 101 } 102 103 104 /** 105 * Connect to the db if the connection does not exist yet. 106 * 107 * @param[in,out] pg the database state 108 * @return #GNUNET_OK on success 109 */ 110 static enum GNUNET_GenericReturnValue 111 internal_setup (struct TALER_EXCHANGEDB_PostgresContext *pg) 112 { 113 struct GNUNET_PQ_Context *db_conn; 114 115 if (NULL != pg->conn) 116 return GNUNET_OK; 117 db_conn = GNUNET_PQ_init (pg->cfg, 118 "exchangedb-postgres", 119 &reconnect_cb, 120 pg); 121 if (NULL == db_conn) 122 return GNUNET_SYSERR; 123 if (0 == TEH_PG_prep_gen_) 124 { 125 GNUNET_PQ_disconnect (db_conn); 126 return GNUNET_SYSERR; 127 } 128 pg->conn = db_conn; 129 return GNUNET_OK; 130 } 131 132 133 /** 134 * Initialize the database connection. 135 * 136 * @param cfg configuration to use 137 * @param check_current true to check if the database schema is current 138 * @return NULL on failure 139 */ 140 static struct TALER_EXCHANGEDB_PostgresContext * 141 do_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, 142 bool check_current) 143 { 144 struct TALER_EXCHANGEDB_PostgresContext *pg; 145 unsigned long long dpl; 146 147 pg = GNUNET_new (struct TALER_EXCHANGEDB_PostgresContext); 148 pg->cfg = cfg; 149 if (GNUNET_OK != 150 GNUNET_CONFIGURATION_get_value_filename (cfg, 151 "exchangedb-postgres", 152 "SQL_DIR", 153 &pg->sql_dir)) 154 { 155 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 156 "exchangedb-postgres", 157 "SQL_DIR"); 158 goto fail; 159 } 160 if (GNUNET_OK != 161 GNUNET_CONFIGURATION_get_value_string (cfg, 162 "exchange", 163 "BASE_URL", 164 &pg->exchange_url)) 165 { 166 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 167 "exchange", 168 "BASE_URL"); 169 goto fail; 170 } 171 if (GNUNET_OK != 172 GNUNET_CONFIGURATION_get_value_time (cfg, 173 "exchangedb", 174 "IDLE_RESERVE_EXPIRATION_TIME", 175 &pg->idle_reserve_expiration_time)) 176 { 177 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 178 "exchangedb", 179 "IDLE_RESERVE_EXPIRATION_TIME"); 180 goto fail; 181 } 182 if (GNUNET_OK != 183 GNUNET_CONFIGURATION_get_value_time (cfg, 184 "exchangedb", 185 "MAX_AML_PROGRAM_RUNTIME", 186 &pg->max_aml_program_runtime)) 187 { 188 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 189 "exchangedb", 190 "MAX_AML_PROGRAM_RUNTIME"); 191 goto fail; 192 } 193 if (GNUNET_OK != 194 GNUNET_CONFIGURATION_get_value_time (cfg, 195 "exchangedb", 196 "LEGAL_RESERVE_EXPIRATION_TIME", 197 &pg->legal_reserve_expiration_time)) 198 { 199 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 200 "exchangedb", 201 "LEGAL_RESERVE_EXPIRATION_TIME"); 202 goto fail; 203 } 204 if (GNUNET_OK != 205 GNUNET_CONFIGURATION_get_value_time (cfg, 206 "exchangedb", 207 "AGGREGATOR_SHIFT", 208 &pg->aggregator_shift)) 209 { 210 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 211 "exchangedb", 212 "AGGREGATOR_SHIFT"); 213 } 214 if (GNUNET_OK != 215 GNUNET_CONFIGURATION_get_value_number (cfg, 216 "exchangedb", 217 "DEFAULT_PURSE_LIMIT", 218 &dpl)) 219 { 220 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 221 "exchangedb", 222 "DEFAULT_PURSE_LIMIT"); 223 pg->def_purse_limit = 1; 224 } 225 else 226 { 227 pg->def_purse_limit = (uint32_t) dpl; 228 } 229 230 if (GNUNET_OK != 231 TALER_config_get_currency (cfg, 232 "exchange", 233 &pg->currency)) 234 { 235 goto fail; 236 } 237 if (GNUNET_OK != 238 internal_setup (pg)) 239 { 240 goto fail; 241 } 242 if (check_current && 243 (GNUNET_OK != 244 GNUNET_PQ_check_current (pg->conn, 245 "exchange-")) ) 246 { 247 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 248 "Database schema is not up-to-date. Try running taler-exchange-dbinit or taler-exchange-dbconfig!\n"); 249 goto fail; 250 } 251 return pg; 252 253 fail: 254 TALER_EXCHANGEDB_disconnect (pg); 255 return NULL; 256 } 257 258 259 struct TALER_EXCHANGEDB_PostgresContext * 260 TALER_EXCHANGEDB_connect ( 261 const struct GNUNET_CONFIGURATION_Handle *cfg) 262 { 263 return do_connect (cfg, 264 true); 265 } 266 267 268 struct TALER_EXCHANGEDB_PostgresContext * 269 TALER_EXCHANGEDB_connect_admin ( 270 const struct GNUNET_CONFIGURATION_Handle *cfg) 271 { 272 return do_connect (cfg, 273 false); 274 } 275 276 277 void 278 TALER_EXCHANGEDB_disconnect (struct TALER_EXCHANGEDB_PostgresContext *pg) 279 { 280 if (NULL == pg) 281 return; 282 if (NULL != pg->conn) 283 { 284 GNUNET_PQ_disconnect (pg->conn); 285 pg->conn = NULL; 286 } 287 GNUNET_free (pg->exchange_url); 288 GNUNET_free (pg->sql_dir); 289 GNUNET_free (pg->currency); 290 GNUNET_free (pg); 291 } 292 293 294 /* end of plugin_exchangedb_postgres.c */