anastasis-db_pg.c (7130B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2020, 2021, 2022 Anastasis SARL 4 5 Anastasis is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Anastasis 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 Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file stasis/anastasis-db_pg.c 18 * @brief shared database state and helpers for the Anastasis postgres backend 19 * @author Christian Grothoff 20 * @author Marcello Stanisci 21 */ 22 #include "platform.h" 23 #include "anastasis-db_pg.h" 24 #include "anastasis/anastasis-database/preflight.h" 25 #include "anastasis/anastasis-database/transaction.h" 26 #include "anastasis/anastasis-database/event.h" 27 #include <taler/taler_pq_lib.h> 28 29 30 /** 31 * Prepared statements have been initialized in this edition. 32 */ 33 uint64_t ANASTASIS_DB_prep_gen_; 34 35 /** 36 * Global database state. 37 */ 38 struct PostgresClosure *pg; 39 40 41 enum GNUNET_GenericReturnValue 42 ANASTASIS_DB_start (const char *name) 43 { 44 struct GNUNET_PQ_ExecuteStatement es[] = { 45 GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"), 46 GNUNET_PQ_EXECUTE_STATEMENT_END 47 }; 48 49 GNUNET_break (GNUNET_OK == 50 ANASTASIS_DB_preflight ()); 51 pg->transaction_name = name; 52 if (GNUNET_OK != 53 GNUNET_PQ_exec_statements (pg->conn, 54 es)) 55 { 56 TALER_LOG_ERROR ("Failed to start transaction\n"); 57 GNUNET_break (0); 58 return GNUNET_SYSERR; 59 } 60 return GNUNET_OK; 61 } 62 63 64 void 65 ANASTASIS_DB_rollback (void) 66 { 67 struct GNUNET_PQ_ExecuteStatement es[] = { 68 GNUNET_PQ_make_execute ("ROLLBACK"), 69 GNUNET_PQ_EXECUTE_STATEMENT_END 70 }; 71 72 if (GNUNET_OK != 73 GNUNET_PQ_exec_statements (pg->conn, 74 es)) 75 { 76 TALER_LOG_ERROR ("Failed to rollback transaction\n"); 77 GNUNET_break (0); 78 } 79 pg->transaction_name = NULL; 80 } 81 82 83 enum GNUNET_DB_QueryStatus 84 ANASTASIS_DB_commit (void) 85 { 86 enum GNUNET_DB_QueryStatus qs; 87 struct GNUNET_PQ_QueryParam no_params[] = { 88 GNUNET_PQ_query_param_end 89 }; 90 91 PREPARE ("do_commit", 92 "COMMIT"); 93 qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, 94 "do_commit", 95 no_params); 96 pg->transaction_name = NULL; 97 return qs; 98 } 99 100 101 struct GNUNET_DB_EventHandler * 102 ANASTASIS_DB_event_listen (const struct GNUNET_DB_EventHeaderP *es, 103 struct GNUNET_TIME_Relative timeout, 104 GNUNET_DB_EventCallback cb, 105 void *cb_cls) 106 { 107 return GNUNET_PQ_event_listen (pg->conn, 108 es, 109 timeout, 110 cb, 111 cb_cls); 112 } 113 114 115 void 116 ANASTASIS_DB_event_listen_cancel (struct GNUNET_DB_EventHandler *eh) 117 { 118 GNUNET_PQ_event_listen_cancel (eh); 119 } 120 121 122 void 123 ANASTASIS_DB_event_notify (const struct GNUNET_DB_EventHeaderP *es, 124 const void *extra, 125 size_t extra_size) 126 { 127 GNUNET_PQ_event_notify (pg->conn, 128 es, 129 extra, 130 extra_size); 131 } 132 133 134 /** 135 * Function called each time we connect or reconnect to the 136 * database. Gives the application a chance to run some 137 * per-connection initialization logic. 138 * 139 * @param cls unused, NULL 140 * @param pq database connection handle 141 */ 142 static void 143 reconnect_cb (void *cls, 144 struct GNUNET_PQ_Context *pq) 145 { 146 #if AUTO_EXPLAIN 147 /* Enable verbose logging to see where queries do not 148 properly use indices */ 149 struct GNUNET_PQ_ExecuteStatement es[] = { 150 GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"), 151 GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"), 152 GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"), 153 GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"), 154 /* https://wiki.postgresql.org/wiki/Serializable suggests to really 155 force the default to 'serializable' if SSI is to be used. */ 156 GNUNET_PQ_make_try_execute ( 157 "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), 158 GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), 159 GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), 160 GNUNET_PQ_make_execute ("SET search_path TO anastasis;"), 161 GNUNET_PQ_EXECUTE_STATEMENT_END 162 }; 163 #else 164 struct GNUNET_PQ_ExecuteStatement es[] = { 165 GNUNET_PQ_make_execute ("SET search_path TO anastasis;"), 166 GNUNET_PQ_EXECUTE_STATEMENT_END 167 }; 168 #endif 169 170 (void) cls; 171 if (GNUNET_OK != 172 GNUNET_PQ_exec_statements (pq, 173 es)) 174 { 175 GNUNET_break (0); 176 return; 177 } 178 ANASTASIS_DB_prep_gen_++; 179 } 180 181 182 /** 183 * Initialize the database connection. 184 * 185 * @param cfg configuration to use 186 * @param check_current true to check if the database schema is current 187 * @return NULL on failure 188 */ 189 static enum GNUNET_GenericReturnValue 190 do_init (const struct GNUNET_CONFIGURATION_Handle *cfg, 191 bool check_current) 192 { 193 pg = GNUNET_new (struct PostgresClosure); 194 pg->cfg = cfg; 195 if (GNUNET_OK != 196 GNUNET_CONFIGURATION_get_value_string (cfg, 197 "anastasis", 198 "CURRENCY", 199 &pg->currency)) 200 { 201 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 202 "anastasis", 203 "CURRENCY"); 204 GNUNET_free (pg); 205 return GNUNET_SYSERR; 206 } 207 pg->conn = GNUNET_PQ_init (pg->cfg, 208 "stasis-postgres", 209 &reconnect_cb, 210 pg); 211 if (NULL == pg->conn) 212 { 213 GNUNET_break (0); 214 ANASTASIS_DB_fini (); 215 return GNUNET_SYSERR; 216 } 217 if (check_current && 218 (GNUNET_OK != 219 GNUNET_PQ_check_current (pg->conn, 220 "stasis-")) ) 221 { 222 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 223 "Database schema is not up-to-date. Try running anastasis-dbinit or anastasis-dbconfig!\n"); 224 ANASTASIS_DB_fini (); 225 return GNUNET_NO; 226 } 227 return GNUNET_OK; 228 } 229 230 231 enum GNUNET_GenericReturnValue 232 ANASTASIS_DB_init (const struct GNUNET_CONFIGURATION_Handle *cfg) 233 { 234 return do_init (cfg, 235 true); 236 } 237 238 239 enum GNUNET_GenericReturnValue 240 ANASTASIS_DB_init_admin (const struct GNUNET_CONFIGURATION_Handle *cfg) 241 { 242 return do_init (cfg, 243 false); 244 } 245 246 247 void 248 ANASTASIS_DB_fini (void) 249 { 250 if (NULL == pg) 251 return; 252 if (NULL != pg->conn) 253 GNUNET_PQ_disconnect (pg->conn); 254 GNUNET_free (pg->currency); 255 GNUNET_free (pg); 256 } 257 258 259 /* end of anastasis-db_pg.c */