aboutsummaryrefslogtreecommitdiff
path: root/src/pq/pq_connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pq/pq_connect.c')
-rw-r--r--src/pq/pq_connect.c478
1 files changed, 0 insertions, 478 deletions
diff --git a/src/pq/pq_connect.c b/src/pq/pq_connect.c
deleted file mode 100644
index a2dce3fb0..000000000
--- a/src/pq/pq_connect.c
+++ /dev/null
@@ -1,478 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2019, 2020 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file pq/pq_connect.c
22 * @brief functions to connect to libpq (PostGres)
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "pq.h"
27#include <pthread.h>
28
29
30/**
31 * Function called by libpq whenever it wants to log something.
32 * We already log whenever we care, so this function does nothing
33 * and merely exists to silence the libpq logging.
34 *
35 * @param arg the SQL connection that was used
36 * @param res information about some libpq event
37 */
38static void
39pq_notice_receiver_cb (void *arg,
40 const PGresult *res)
41{
42 /* do nothing, intentionally */
43 (void) arg;
44 (void) res;
45}
46
47
48/**
49 * Function called by libpq whenever it wants to log something.
50 * We log those using the GNUnet logger.
51 *
52 * @param arg the SQL connection that was used
53 * @param message information about some libpq event
54 */
55static void
56pq_notice_processor_cb (void *arg,
57 const char *message)
58{
59 (void) arg;
60 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
61 "pq",
62 "%s",
63 message);
64}
65
66
67struct GNUNET_PQ_Context *
68GNUNET_PQ_connect (const char *config_str,
69 const char *load_path,
70 const struct GNUNET_PQ_ExecuteStatement *es,
71 const struct GNUNET_PQ_PreparedStatement *ps)
72{
73 struct GNUNET_PQ_Context *db;
74 unsigned int elen = 0;
75 unsigned int plen = 0;
76
77 if (NULL != es)
78 while (NULL != es[elen].sql)
79 elen++;
80 if (NULL != ps)
81 while (NULL != ps[plen].name)
82 plen++;
83
84 db = GNUNET_new (struct GNUNET_PQ_Context);
85 db->config_str = GNUNET_strdup (config_str);
86 if (NULL != load_path)
87 db->load_path = GNUNET_strdup (load_path);
88 if (0 != elen)
89 {
90 db->es = GNUNET_new_array (elen + 1,
91 struct GNUNET_PQ_ExecuteStatement);
92 memcpy (db->es,
93 es,
94 elen * sizeof (struct GNUNET_PQ_ExecuteStatement));
95 }
96 if (0 != plen)
97 {
98 db->ps = GNUNET_new_array (plen + 1,
99 struct GNUNET_PQ_PreparedStatement);
100 memcpy (db->ps,
101 ps,
102 plen * sizeof (struct GNUNET_PQ_PreparedStatement));
103 }
104 db->channel_map = GNUNET_CONTAINER_multishortmap_create (16,
105 GNUNET_YES);
106 GNUNET_PQ_reconnect (db);
107 if (NULL == db->conn)
108 {
109 GNUNET_free (db->load_path);
110 GNUNET_free (db->config_str);
111 GNUNET_free (db);
112 return NULL;
113 }
114 return db;
115}
116
117
118/**
119 * Apply patch number @a from path @a load_path.
120 *
121 * @param db database context to use
122 * @param load_path where to find the SQL code to run
123 * @param i patch number to append to the @a load_path
124 * @return #GNUNET_OK on success, #GNUNET_NO if patch @a i does not exist, #GNUNET_SYSERR on error
125 */
126static enum GNUNET_GenericReturnValue
127apply_patch (struct GNUNET_PQ_Context *db,
128 const char *load_path,
129 unsigned int i)
130{
131 struct GNUNET_OS_Process *psql;
132 enum GNUNET_OS_ProcessStatusType type;
133 unsigned long code;
134 size_t slen = strlen (load_path) + 10;
135 char buf[slen];
136
137 GNUNET_snprintf (buf,
138 sizeof (buf),
139 "%s%04u.sql",
140 load_path,
141 i);
142 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
143 "Applying SQL file `%s' on database %s\n",
144 buf,
145 db->config_str);
146 psql = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
147 NULL,
148 NULL,
149 NULL,
150 "psql",
151 "psql",
152 db->config_str,
153 "-f",
154 buf,
155 "-q",
156 "--set",
157 "ON_ERROR_STOP=1",
158 NULL);
159 if (NULL == psql)
160 {
161 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
162 "exec",
163 "psql");
164 return GNUNET_SYSERR;
165 }
166 GNUNET_assert (GNUNET_OK ==
167 GNUNET_OS_process_wait_status (psql,
168 &type,
169 &code));
170 GNUNET_OS_process_destroy (psql);
171 if ( (GNUNET_OS_PROCESS_EXITED != type) ||
172 (0 != code) )
173 {
174 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
175 "Could not run PSQL on file %s: psql exit code was %d\n",
176 buf,
177 (int) code);
178 return GNUNET_SYSERR;
179 }
180 return GNUNET_OK;
181}
182
183
184enum GNUNET_GenericReturnValue
185GNUNET_PQ_run_sql (struct GNUNET_PQ_Context *db,
186 const char *load_path)
187{
188 const char *load_path_suffix;
189 size_t slen = strlen (load_path) + 10;
190
191 load_path_suffix = strrchr (load_path, '/');
192 if (NULL == load_path_suffix)
193 {
194 GNUNET_break (0);
195 return GNUNET_SYSERR;
196 }
197 load_path_suffix++; /* skip '/' */
198 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
199 "Loading SQL resources from `%s'\n",
200 load_path);
201 for (unsigned int i = 1; i<10000; i++)
202 {
203 enum GNUNET_DB_QueryStatus qs;
204 {
205 char buf[slen];
206
207 /* First, check patch actually exists */
208 GNUNET_snprintf (buf,
209 sizeof (buf),
210 "%s%04u.sql",
211 load_path,
212 i);
213 if (GNUNET_YES !=
214 GNUNET_DISK_file_test (buf))
215 return GNUNET_OK; /* We are done */
216 }
217
218 /* Second, check with DB versioning schema if this patch was already applied,
219 if so, skip it. */
220 {
221 char patch_name[slen];
222
223 GNUNET_snprintf (patch_name,
224 sizeof (patch_name),
225 "%s%04u",
226 load_path_suffix,
227 i);
228 {
229 char *applied_by;
230 struct GNUNET_PQ_QueryParam params[] = {
231 GNUNET_PQ_query_param_string (patch_name),
232 GNUNET_PQ_query_param_end
233 };
234 struct GNUNET_PQ_ResultSpec rs[] = {
235 GNUNET_PQ_result_spec_string ("applied_by",
236 &applied_by),
237 GNUNET_PQ_result_spec_end
238 };
239
240 qs = GNUNET_PQ_eval_prepared_singleton_select (db,
241 "gnunet_pq_check_patch",
242 params,
243 rs);
244 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
245 {
246 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
247 "Database version %s already applied by %s, skipping\n",
248 patch_name,
249 applied_by);
250 GNUNET_PQ_cleanup_result (rs);
251 }
252 if (GNUNET_DB_STATUS_HARD_ERROR == qs)
253 {
254 GNUNET_break (0);
255 return GNUNET_SYSERR;
256 }
257 }
258 }
259 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
260 continue; /* patch already applied, skip it */
261
262 /* patch not yet applied, run it! */
263 {
264 int ret;
265
266 ret = apply_patch (db,
267 load_path,
268 i);
269 if (GNUNET_NO == ret)
270 break;
271 if (GNUNET_SYSERR == ret)
272 return GNUNET_SYSERR;
273 }
274 }
275 return GNUNET_OK;
276}
277
278
279void
280GNUNET_PQ_reconnect_if_down (struct GNUNET_PQ_Context *db)
281{
282 if (1 ==
283 PQconsumeInput (db->conn))
284 return;
285 if (CONNECTION_BAD != PQstatus (db->conn))
286 return;
287 GNUNET_PQ_reconnect (db);
288}
289
290
291void
292GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db)
293{
294 GNUNET_PQ_event_reconnect_ (db,
295 -1);
296 if (NULL != db->conn)
297 PQfinish (db->conn);
298 db->conn = PQconnectdb (db->config_str);
299 if ( (NULL == db->conn) ||
300 (CONNECTION_OK != PQstatus (db->conn)) )
301 {
302 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
303 "pq",
304 "Database connection to '%s' failed: %s\n",
305 db->config_str,
306 (NULL != db->conn) ?
307 PQerrorMessage (db->conn)
308 : "PQconnectdb returned NULL");
309 if (NULL != db->conn)
310 {
311 PQfinish (db->conn);
312 db->conn = NULL;
313 }
314 return;
315 }
316 PQsetNoticeReceiver (db->conn,
317 &pq_notice_receiver_cb,
318 db);
319 PQsetNoticeProcessor (db->conn,
320 &pq_notice_processor_cb,
321 db);
322 if (NULL != db->load_path)
323 {
324 PGresult *res;
325
326 res = PQprepare (db->conn,
327 "gnunet_pq_check_patch",
328 "SELECT"
329 " applied_by"
330 " FROM _v.patches"
331 " WHERE patch_name = $1"
332 " LIMIT 1",
333 1,
334 NULL);
335 if (PGRES_COMMAND_OK != PQresultStatus (res))
336 {
337 int ret;
338
339 PQclear (res);
340 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
341 "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet, loading patch level 0000!\n");
342 ret = apply_patch (db,
343 db->load_path,
344 0);
345 if (GNUNET_NO == ret)
346 {
347 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
348 "Failed to find SQL file to load database versioning logic\n");
349 PQfinish (db->conn);
350 db->conn = NULL;
351 return;
352 }
353 if (GNUNET_SYSERR == ret)
354 {
355 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
356 "Failed to run SQL logic to setup database versioning logic\n");
357 PQfinish (db->conn);
358 db->conn = NULL;
359 return;
360 }
361 /* try again to prepare our statement! */
362 res = PQprepare (db->conn,
363 "gnunet_pq_check_patch",
364 "SELECT"
365 " applied_by"
366 " FROM _v.patches"
367 " WHERE patch_name = $1"
368 " LIMIT 1",
369 1,
370 NULL);
371 if (PGRES_COMMAND_OK != PQresultStatus (res))
372 {
373 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
374 "Failed to run SQL logic to setup database versioning logic: %s/%s\n",
375 PQresultErrorMessage (res),
376 PQerrorMessage (db->conn));
377 PQclear (res);
378 PQfinish (db->conn);
379 db->conn = NULL;
380 return;
381 }
382 }
383 PQclear (res);
384
385 if (GNUNET_SYSERR ==
386 GNUNET_PQ_run_sql (db,
387 db->load_path))
388 {
389 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
390 "Failed to load SQL statements from `%s*'\n",
391 db->load_path);
392 PQfinish (db->conn);
393 db->conn = NULL;
394 return;
395 }
396 }
397 if ( (NULL != db->es) &&
398 (GNUNET_OK !=
399 GNUNET_PQ_exec_statements (db,
400 db->es)) )
401 {
402 PQfinish (db->conn);
403 db->conn = NULL;
404 return;
405 }
406 if ( (NULL != db->ps) &&
407 (GNUNET_OK !=
408 GNUNET_PQ_prepare_statements (db,
409 db->ps)) )
410 {
411 PQfinish (db->conn);
412 db->conn = NULL;
413 return;
414 }
415 GNUNET_PQ_event_reconnect_ (db,
416 PQsocket (db->conn));
417}
418
419
420struct GNUNET_PQ_Context *
421GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
422 const char *section,
423 const char *load_path_suffix,
424 const struct GNUNET_PQ_ExecuteStatement *es,
425 const struct GNUNET_PQ_PreparedStatement *ps)
426{
427 struct GNUNET_PQ_Context *db;
428 char *conninfo;
429 char *load_path;
430 char *sp;
431
432 if (GNUNET_OK !=
433 GNUNET_CONFIGURATION_get_value_string (cfg,
434 section,
435 "CONFIG",
436 &conninfo))
437 conninfo = NULL;
438 load_path = NULL;
439 sp = NULL;
440 if ( (NULL != load_path_suffix) &&
441 (GNUNET_OK ==
442 GNUNET_CONFIGURATION_get_value_filename (cfg,
443 section,
444 "SQL_DIR",
445 &sp)) )
446 GNUNET_asprintf (&load_path,
447 "%s%s",
448 sp,
449 load_path_suffix);
450 db = GNUNET_PQ_connect (conninfo == NULL ? "" : conninfo,
451 load_path,
452 es,
453 ps);
454 GNUNET_free (load_path);
455 GNUNET_free (sp);
456 GNUNET_free (conninfo);
457 return db;
458}
459
460
461void
462GNUNET_PQ_disconnect (struct GNUNET_PQ_Context *db)
463{
464 if (NULL == db)
465 return;
466 GNUNET_assert (0 ==
467 GNUNET_CONTAINER_multishortmap_size (db->channel_map));
468 GNUNET_CONTAINER_multishortmap_destroy (db->channel_map);
469 GNUNET_free (db->es);
470 GNUNET_free (db->ps);
471 GNUNET_free (db->load_path);
472 GNUNET_free (db->config_str);
473 PQfinish (db->conn);
474 GNUNET_free (db);
475}
476
477
478/* end of pq/pq_connect.c */