plugin_donaudb_postgres.c (5889B)
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 struct DONAUDB_PostgresContext; 27 #define GNUNET_PQ_RECONNECT_CALLBACK_CLOSURE \ 28 struct DONAUDB_PostgresContext 29 #include "helper.h" 30 31 /** 32 * Set to 1 to enable Postgres auto_explain module. This will 33 * slow down things a _lot_, but also provide extensive logging 34 * in the Postgres database logger for performance analysis. 35 */ 36 #define AUTO_EXPLAIN 0 37 38 39 /** 40 * Function called each time we connect or reconnect to the 41 * database. Gives the application a chance to run some 42 * per-connection initialization logic. 43 * 44 * @param pg database conntext in the donau 45 * @param pq database connection handle 46 */ 47 static void 48 reconnect_cb (struct DONAUDB_PostgresContext *pg, 49 struct GNUNET_PQ_Context *pq) 50 { 51 #if AUTO_EXPLAIN 52 /* Enable verbose logging to see where queries do not 53 properly use indices */ 54 struct GNUNET_PQ_ExecuteStatement es[] = { 55 GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"), 56 GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"), 57 GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"), 58 GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"), 59 /* https://wiki.postgresql.org/wiki/Serializable suggests to really 60 force the default to 'serializable' if SSI is to be used. */ 61 GNUNET_PQ_make_try_execute ( 62 "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), 63 GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), 64 GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), 65 GNUNET_PQ_make_try_execute ("SET search_path TO donau;"), 66 GNUNET_PQ_EXECUTE_STATEMENT_END 67 }; 68 #else 69 struct GNUNET_PQ_ExecuteStatement es[] = { 70 GNUNET_PQ_make_try_execute ( 71 "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), 72 GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), 73 GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), 74 GNUNET_PQ_make_try_execute ("SET autocommit=OFF;"), 75 GNUNET_PQ_make_try_execute ("SET search_path TO donau;"), 76 GNUNET_PQ_EXECUTE_STATEMENT_END 77 }; 78 #endif 79 80 if (GNUNET_OK != 81 GNUNET_PQ_exec_statements (pq, 82 es)) 83 { 84 GNUNET_break (0); 85 return; 86 } 87 pg->prep_gen++; 88 } 89 90 91 /** 92 * Connect to the database if the connection does not exist yet. 93 * 94 * @param pg the plugin-specific state 95 * @return #GNUNET_OK on success 96 */ 97 static enum GNUNET_GenericReturnValue 98 internal_setup (struct DONAUDB_PostgresContext *pg) 99 { 100 struct GNUNET_PQ_Context *db_conn; 101 102 if (NULL != pg->conn) 103 return GNUNET_OK; 104 db_conn = GNUNET_PQ_init (pg->cfg, 105 "donaudb-postgres", 106 &reconnect_cb, 107 pg); 108 if (NULL == db_conn) 109 return GNUNET_SYSERR; 110 if (0 == pg->prep_gen) 111 { 112 GNUNET_PQ_disconnect (db_conn); 113 return GNUNET_SYSERR; 114 } 115 pg->conn = db_conn; 116 return GNUNET_OK; 117 } 118 119 120 /** 121 * Initialize the database connection. 122 * 123 * @param cfg configuration to use 124 * @param check_current true to check if the database schema is current 125 * @return NULL on failure 126 */ 127 static struct DONAUDB_PostgresContext * 128 do_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, 129 bool check_current) 130 { 131 struct DONAUDB_PostgresContext *pg; 132 133 pg = GNUNET_new (struct DONAUDB_PostgresContext); 134 pg->cfg = cfg; 135 if (GNUNET_OK != 136 GNUNET_CONFIGURATION_get_value_string (cfg, 137 "donau", 138 "BASE_URL", 139 &pg->donau_url)) 140 { 141 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 142 "donau", 143 "BASE_URL"); 144 goto fail; 145 } 146 if (GNUNET_OK != 147 TALER_config_get_currency (cfg, 148 "donau", 149 &pg->currency)) 150 { 151 goto fail; 152 } 153 if (GNUNET_OK != 154 internal_setup (pg)) 155 goto fail; 156 if (check_current && 157 (GNUNET_OK != 158 GNUNET_PQ_check_current (pg->conn, 159 "donau-")) ) 160 { 161 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 162 "Database schema is not up-to-date. Try running donau-dbinit or donau-dbconfig!\n"); 163 goto fail; 164 } 165 166 return pg; 167 fail: 168 DONAUDB_disconnect (pg); 169 return NULL; 170 } 171 172 173 struct DONAUDB_PostgresContext * 174 DONAUDB_connect ( 175 const struct GNUNET_CONFIGURATION_Handle *cfg) 176 { 177 return do_connect (cfg, 178 true); 179 } 180 181 182 struct DONAUDB_PostgresContext * 183 DONAUDB_connect_admin ( 184 const struct GNUNET_CONFIGURATION_Handle *cfg) 185 { 186 return do_connect (cfg, 187 false); 188 } 189 190 191 void 192 DONAUDB_disconnect (struct DONAUDB_PostgresContext *pg) 193 { 194 if (NULL == pg) 195 return; 196 if (NULL != pg->conn) 197 { 198 GNUNET_PQ_disconnect (pg->conn); 199 pg->conn = NULL; 200 } 201 GNUNET_free (pg->donau_url); 202 GNUNET_free (pg->currency); 203 GNUNET_free (pg); 204 } 205 206 207 /* end of plugin_donaudb_postgres.c */