pg.c (5647B)
1 /* 2 This file is part of Challenger 3 (C) 2023 Taler Systems SA 4 5 Challenger is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Lesser General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Challenger is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of ANASTASISABILITY 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 Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file challengerdb/plugin_challengerdb_postgres.c 18 * @brief database helper functions for postgres used by challenger 19 * @author Christian Grothoff 20 */ 21 struct CHALLENGERDB_PostgresContext; 22 #define GNUNET_PQ_RECONNECT_CALLBACK_CLOSURE \ 23 struct CHALLENGERDB_PostgresContext 24 #include <gnunet/gnunet_util_lib.h> 25 #include <gnunet/gnunet_db_lib.h> 26 #include <gnunet/gnunet_pq_lib.h> 27 #include <taler/taler_pq_lib.h> 28 #include "challenger_database_lib.h" 29 #include "challenger_util.h" 30 #include "pg_helper.h" 31 32 /** 33 * Counts how often we have established a fresh @e conn 34 * to the database. Used to re-prepare statements. 35 */ 36 unsigned long long CH_PG_prep_gen_; 37 38 /** 39 * Function called each time we connect or reconnect to the 40 * database. Gives the application a chance to run some 41 * per-connection initialization logic. 42 * 43 * @param pg database conntext in the auditor 44 * @param pq database connection handle 45 */ 46 static void 47 reconnect_cb (struct CHALLENGERDB_PostgresContext *pg, 48 struct GNUNET_PQ_Context *pq) 49 { 50 #if AUTO_EXPLAIN 51 /* Enable verbose logging to see where queries do not 52 properly use indices */ 53 struct GNUNET_PQ_ExecuteStatement es[] = { 54 GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"), 55 GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"), 56 GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"), 57 GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"), 58 /* https://wiki.postgresql.org/wiki/Serializable suggests to really 59 force the default to 'serializable' if SSI is to be used. */ 60 GNUNET_PQ_make_try_execute ( 61 "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), 62 GNUNET_PQ_make_execute ("SET search_path TO challenger;"), 63 GNUNET_PQ_EXECUTE_STATEMENT_END 64 }; 65 #else 66 struct GNUNET_PQ_ExecuteStatement es[] = { 67 /* https://wiki.postgresql.org/wiki/Serializable suggests to really 68 force the default to 'serializable' if SSI is to be used. */ 69 GNUNET_PQ_make_try_execute ( 70 "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), 71 GNUNET_PQ_make_execute ("SET search_path TO challenger;"), 72 GNUNET_PQ_EXECUTE_STATEMENT_END 73 }; 74 #endif 75 76 if (GNUNET_OK != 77 GNUNET_PQ_exec_statements (pq, 78 es)) 79 { 80 GNUNET_break (0); 81 return; 82 } 83 CH_PG_prep_gen_++; 84 } 85 86 87 /** 88 * Connect to the db if the connection does not exist yet. 89 * 90 * @param[in,out] pg the database state 91 * @return #GNUNET_OK on success 92 */ 93 static enum GNUNET_GenericReturnValue 94 internal_setup (struct CHALLENGERDB_PostgresContext *pg) 95 { 96 struct GNUNET_PQ_Context *db_conn; 97 98 if (NULL != pg->conn) 99 return GNUNET_OK; 100 db_conn = GNUNET_PQ_init (pg->cfg, 101 "challengerdb-postgres", 102 &reconnect_cb, 103 pg); 104 if (NULL == db_conn) 105 return GNUNET_SYSERR; 106 if (0 == CH_PG_prep_gen_) 107 { 108 GNUNET_PQ_disconnect (db_conn); 109 return GNUNET_SYSERR; 110 } 111 pg->conn = db_conn; 112 return GNUNET_OK; 113 } 114 115 116 static struct CHALLENGERDB_PostgresContext * 117 do_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, 118 bool check_current) 119 { 120 struct CHALLENGERDB_PostgresContext *pg; 121 122 pg = GNUNET_new (struct CHALLENGERDB_PostgresContext); 123 pg->cfg = cfg; 124 if (GNUNET_OK != 125 GNUNET_CONFIGURATION_get_value_filename (cfg, 126 "challengerdb-postgres", 127 "SQL_DIR", 128 &pg->sql_dir)) 129 { 130 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 131 "challengerdb-postgres", 132 "SQL_DIR"); 133 GNUNET_free (pg); 134 return NULL; 135 } 136 if (GNUNET_OK != 137 internal_setup (pg)) 138 { 139 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 140 "Failed to ensure DB is initialized\n"); 141 CHALLENGERDB_disconnect (pg); 142 return NULL; 143 } 144 if (check_current && 145 (GNUNET_OK != 146 GNUNET_PQ_check_current (pg->conn, 147 "challenger-")) ) 148 { 149 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 150 "Database schema is not up-to-date. Try running challenger-dbinit or challenger-dbconfig!\n"); 151 CHALLENGERDB_disconnect (pg); 152 return NULL; 153 } 154 return pg; 155 } 156 157 158 struct CHALLENGERDB_PostgresContext * 159 CHALLENGERDB_connect ( 160 const struct GNUNET_CONFIGURATION_Handle *cfg) 161 { 162 return do_connect (cfg, 163 true); 164 } 165 166 167 struct CHALLENGERDB_PostgresContext * 168 CHALLENGERDB_connect_admin ( 169 const struct GNUNET_CONFIGURATION_Handle *cfg) 170 { 171 return do_connect (cfg, 172 false); 173 } 174 175 176 void 177 CHALLENGERDB_disconnect (struct CHALLENGERDB_PostgresContext *pg) 178 { 179 if (NULL == pg) 180 return; 181 if (NULL != pg->conn) 182 GNUNET_PQ_disconnect (pg->conn); 183 GNUNET_free (pg->sql_dir); 184 GNUNET_free (pg); 185 } 186 187 188 /* end of pg.c */