Line data Source code
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 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 : Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file src/challengerdb/pg.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 challenger database context
44 : * @param pq database connection handle
45 : */
46 : static void
47 50 : 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 50 : 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 50 : GNUNET_PQ_make_try_execute (
70 : "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
71 50 : GNUNET_PQ_make_execute ("SET search_path TO challenger;"),
72 : GNUNET_PQ_EXECUTE_STATEMENT_END
73 : };
74 : #endif
75 :
76 50 : if (GNUNET_OK !=
77 50 : GNUNET_PQ_exec_statements (pq,
78 : es))
79 : {
80 0 : GNUNET_break (0);
81 0 : return;
82 : }
83 50 : 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 50 : internal_setup (struct CHALLENGERDB_PostgresContext *pg)
95 : {
96 : struct GNUNET_PQ_Context *db_conn;
97 :
98 50 : if (NULL != pg->conn)
99 0 : return GNUNET_OK;
100 50 : db_conn = GNUNET_PQ_init (pg->cfg,
101 : "challengerdb-postgres",
102 : &reconnect_cb,
103 : pg);
104 50 : if (NULL == db_conn)
105 0 : return GNUNET_SYSERR;
106 50 : if (0 == CH_PG_prep_gen_)
107 : {
108 0 : GNUNET_PQ_disconnect (db_conn);
109 0 : return GNUNET_SYSERR;
110 : }
111 50 : pg->conn = db_conn;
112 50 : return GNUNET_OK;
113 : }
114 :
115 :
116 : static struct CHALLENGERDB_PostgresContext *
117 50 : do_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
118 : bool check_current)
119 : {
120 : struct CHALLENGERDB_PostgresContext *pg;
121 :
122 50 : pg = GNUNET_new (struct CHALLENGERDB_PostgresContext);
123 50 : pg->cfg = cfg;
124 50 : if (GNUNET_OK !=
125 50 : GNUNET_CONFIGURATION_get_value_filename (cfg,
126 : "challengerdb-postgres",
127 : "SQL_DIR",
128 : &pg->sql_dir))
129 : {
130 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
131 : "challengerdb-postgres",
132 : "SQL_DIR");
133 0 : GNUNET_free (pg);
134 0 : return NULL;
135 : }
136 50 : if (GNUNET_OK !=
137 50 : internal_setup (pg))
138 : {
139 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
140 : "Failed to ensure DB is initialized\n");
141 0 : CHALLENGERDB_disconnect (pg);
142 0 : return NULL;
143 : }
144 84 : if (check_current &&
145 : (GNUNET_OK !=
146 34 : GNUNET_PQ_check_current (pg->conn,
147 : "challenger-")) )
148 : {
149 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
150 : "Database schema is not up-to-date. Try running challenger-dbinit or challenger-dbconfig!\n");
151 0 : CHALLENGERDB_disconnect (pg);
152 0 : return NULL;
153 : }
154 50 : return pg;
155 : }
156 :
157 :
158 : struct CHALLENGERDB_PostgresContext *
159 34 : CHALLENGERDB_connect (
160 : const struct GNUNET_CONFIGURATION_Handle *cfg)
161 : {
162 34 : return do_connect (cfg,
163 : true);
164 : }
165 :
166 :
167 : struct CHALLENGERDB_PostgresContext *
168 16 : CHALLENGERDB_connect_admin (
169 : const struct GNUNET_CONFIGURATION_Handle *cfg)
170 : {
171 16 : return do_connect (cfg,
172 : false);
173 : }
174 :
175 :
176 : void
177 50 : CHALLENGERDB_disconnect (struct CHALLENGERDB_PostgresContext *pg)
178 : {
179 50 : if (NULL == pg)
180 0 : return;
181 50 : if (NULL != pg->conn)
182 50 : GNUNET_PQ_disconnect (pg->conn);
183 50 : GNUNET_free (pg->sql_dir);
184 50 : GNUNET_free (pg);
185 : }
186 :
187 :
188 : /* end of pg.c */
|