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.c525
1 files changed, 0 insertions, 525 deletions
diff --git a/src/pq/pq_connect.c b/src/pq/pq_connect.c
deleted file mode 100644
index 5fea994ef..000000000
--- a/src/pq/pq_connect.c
+++ /dev/null
@@ -1,525 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2019, 2020, 2021 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 return GNUNET_PQ_connect2 (config_str,
74 load_path,
75 es,
76 ps,
77 GNUNET_PQ_FLAG_NONE);
78}
79
80
81struct GNUNET_PQ_Context *
82GNUNET_PQ_connect2 (const char *config_str,
83 const char *load_path,
84 const struct GNUNET_PQ_ExecuteStatement *es,
85 const struct GNUNET_PQ_PreparedStatement *ps,
86 enum GNUNET_PQ_Options flags)
87{
88 struct GNUNET_PQ_Context *db;
89 unsigned int elen = 0;
90 unsigned int plen = 0;
91
92 if (NULL != es)
93 while (NULL != es[elen].sql)
94 elen++;
95 if (NULL != ps)
96 while (NULL != ps[plen].name)
97 plen++;
98
99 db = GNUNET_new (struct GNUNET_PQ_Context);
100 db->flags = flags;
101 db->config_str = GNUNET_strdup (config_str);
102 if (NULL != load_path)
103 db->load_path = GNUNET_strdup (load_path);
104 if (0 != elen)
105 {
106 db->es = GNUNET_new_array (elen + 1,
107 struct GNUNET_PQ_ExecuteStatement);
108 memcpy (db->es,
109 es,
110 elen * sizeof (struct GNUNET_PQ_ExecuteStatement));
111 }
112 if (0 != plen)
113 {
114 db->ps = GNUNET_new_array (plen + 1,
115 struct GNUNET_PQ_PreparedStatement);
116 memcpy (db->ps,
117 ps,
118 plen * sizeof (struct GNUNET_PQ_PreparedStatement));
119 }
120 db->channel_map = GNUNET_CONTAINER_multishortmap_create (16,
121 GNUNET_YES);
122 GNUNET_PQ_reconnect (db);
123 if (NULL == db->conn)
124 {
125 GNUNET_CONTAINER_multishortmap_destroy (db->channel_map);
126 GNUNET_free (db->load_path);
127 GNUNET_free (db->config_str);
128 GNUNET_free (db);
129 return NULL;
130 }
131 return db;
132}
133
134
135/**
136 * Apply patch number @a from path @a load_path.
137 *
138 * @param db database context to use
139 * @param load_path where to find the SQL code to run
140 * @param i patch number to append to the @a load_path
141 * @return #GNUNET_OK on success, #GNUNET_NO if patch @a i does not exist, #GNUNET_SYSERR on error
142 */
143static enum GNUNET_GenericReturnValue
144apply_patch (struct GNUNET_PQ_Context *db,
145 const char *load_path,
146 unsigned int i)
147{
148 struct GNUNET_OS_Process *psql;
149 enum GNUNET_OS_ProcessStatusType type;
150 unsigned long code;
151 size_t slen = strlen (load_path) + 10;
152 char buf[slen];
153
154 GNUNET_snprintf (buf,
155 sizeof (buf),
156 "%s%04u.sql",
157 load_path,
158 i);
159 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
160 "Applying SQL file `%s' on database %s\n",
161 buf,
162 db->config_str);
163 psql = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
164 NULL,
165 NULL,
166 NULL,
167 "psql",
168 "psql",
169 db->config_str,
170 "-f",
171 buf,
172 "-q",
173 "--set",
174 "ON_ERROR_STOP=1",
175 NULL);
176 if (NULL == psql)
177 {
178 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
179 "exec",
180 "psql");
181 return GNUNET_SYSERR;
182 }
183 GNUNET_assert (GNUNET_OK ==
184 GNUNET_OS_process_wait_status (psql,
185 &type,
186 &code));
187 GNUNET_OS_process_destroy (psql);
188 if ( (GNUNET_OS_PROCESS_EXITED != type) ||
189 (0 != code) )
190 {
191 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
192 "Could not run PSQL on file %s: psql exit code was %d\n",
193 buf,
194 (int) code);
195 return GNUNET_SYSERR;
196 }
197 return GNUNET_OK;
198}
199
200
201enum GNUNET_GenericReturnValue
202GNUNET_PQ_run_sql (struct GNUNET_PQ_Context *db,
203 const char *load_path)
204{
205 const char *load_path_suffix;
206 size_t slen = strlen (load_path) + 10;
207
208 load_path_suffix = strrchr (load_path, '/');
209 if (NULL == load_path_suffix)
210 {
211 GNUNET_break (0);
212 return GNUNET_SYSERR;
213 }
214 load_path_suffix++; /* skip '/' */
215 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
216 "Loading SQL resources from `%s'\n",
217 load_path);
218 for (unsigned int i = 1; i<10000; i++)
219 {
220 char patch_name[slen];
221 char buf[slen];
222 enum GNUNET_DB_QueryStatus qs;
223
224 /* First, check patch actually exists */
225 GNUNET_snprintf (buf,
226 sizeof (buf),
227 "%s%04u.sql",
228 load_path,
229 i);
230 if (GNUNET_YES !=
231 GNUNET_DISK_file_test (buf))
232 return GNUNET_OK; /* We are done */
233
234 /* Second, check with DB versioning schema if this patch was already applied,
235 if so, skip it. */
236 GNUNET_snprintf (patch_name,
237 sizeof (patch_name),
238 "%s%04u",
239 load_path_suffix,
240 i);
241 {
242 char *applied_by;
243 struct GNUNET_PQ_QueryParam params[] = {
244 GNUNET_PQ_query_param_string (patch_name),
245 GNUNET_PQ_query_param_end
246 };
247 struct GNUNET_PQ_ResultSpec rs[] = {
248 GNUNET_PQ_result_spec_string ("applied_by",
249 &applied_by),
250 GNUNET_PQ_result_spec_end
251 };
252
253 qs = GNUNET_PQ_eval_prepared_singleton_select (db,
254 "gnunet_pq_check_patch",
255 params,
256 rs);
257 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
258 {
259 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
260 "Database version %s already applied by %s, skipping\n",
261 patch_name,
262 applied_by);
263 GNUNET_PQ_cleanup_result (rs);
264 }
265 if (GNUNET_DB_STATUS_HARD_ERROR == qs)
266 {
267 GNUNET_break (0);
268 return GNUNET_SYSERR;
269 }
270 }
271 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
272 continue; /* patch already applied, skip it */
273
274 if (0 != (GNUNET_PQ_FLAG_CHECK_CURRENT & db->flags))
275 {
276 /* We are only checking, found unapplied patch, bad! */
277 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
278 "Database outdated, patch %s missing. Aborting!\n",
279 patch_name);
280 return GNUNET_SYSERR;
281 }
282 else
283 {
284 /* patch not yet applied, run it! */
285 enum GNUNET_GenericReturnValue ret;
286
287 ret = apply_patch (db,
288 load_path,
289 i);
290 if (GNUNET_NO == ret)
291 break;
292 if (GNUNET_SYSERR == ret)
293 return GNUNET_SYSERR;
294 }
295 }
296 return GNUNET_OK;
297}
298
299
300void
301GNUNET_PQ_reconnect_if_down (struct GNUNET_PQ_Context *db)
302{
303 if (1 ==
304 PQconsumeInput (db->conn))
305 return;
306 if (CONNECTION_BAD != PQstatus (db->conn))
307 return;
308 GNUNET_PQ_reconnect (db);
309}
310
311
312void
313GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db)
314{
315 GNUNET_PQ_event_reconnect_ (db,
316 -1);
317 if (NULL != db->conn)
318 PQfinish (db->conn);
319 db->conn = PQconnectdb (db->config_str);
320 if ( (NULL == db->conn) ||
321 (CONNECTION_OK != PQstatus (db->conn)) )
322 {
323 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
324 "pq",
325 "Database connection to '%s' failed: %s\n",
326 db->config_str,
327 (NULL != db->conn) ?
328 PQerrorMessage (db->conn)
329 : "PQconnectdb returned NULL");
330 if (NULL != db->conn)
331 {
332 PQfinish (db->conn);
333 db->conn = NULL;
334 }
335 return;
336 }
337 PQsetNoticeReceiver (db->conn,
338 &pq_notice_receiver_cb,
339 db);
340 PQsetNoticeProcessor (db->conn,
341 &pq_notice_processor_cb,
342 db);
343 if (NULL != db->load_path)
344 {
345 PGresult *res;
346
347 res = PQprepare (db->conn,
348 "gnunet_pq_check_patch",
349 "SELECT"
350 " applied_by"
351 " FROM _v.patches"
352 " WHERE patch_name = $1"
353 " LIMIT 1",
354 1,
355 NULL);
356 if (PGRES_COMMAND_OK != PQresultStatus (res))
357 {
358 enum GNUNET_GenericReturnValue ret;
359
360 PQclear (res);
361 if (0 != (db->flags & GNUNET_PQ_FLAG_DROP))
362 {
363 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
364 "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet. Not attempting drop!\n");
365 PQfinish (db->conn);
366 db->conn = NULL;
367 return;
368 }
369 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
370 "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet, loading patch level 0000!\n");
371 ret = apply_patch (db,
372 db->load_path,
373 0);
374 if (GNUNET_NO == ret)
375 {
376 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
377 "Failed to find SQL file to load database versioning logic\n");
378 PQfinish (db->conn);
379 db->conn = NULL;
380 return;
381 }
382 if (GNUNET_SYSERR == ret)
383 {
384 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
385 "Failed to run SQL logic to setup database versioning logic\n");
386 PQfinish (db->conn);
387 db->conn = NULL;
388 return;
389 }
390 /* try again to prepare our statement! */
391 res = PQprepare (db->conn,
392 "gnunet_pq_check_patch",
393 "SELECT"
394 " applied_by"
395 " FROM _v.patches"
396 " WHERE patch_name = $1"
397 " LIMIT 1",
398 1,
399 NULL);
400 if (PGRES_COMMAND_OK != PQresultStatus (res))
401 {
402 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
403 "Failed to run SQL logic to setup database versioning logic: %s/%s\n",
404 PQresultErrorMessage (res),
405 PQerrorMessage (db->conn));
406 PQclear (res);
407 PQfinish (db->conn);
408 db->conn = NULL;
409 return;
410 }
411 }
412 PQclear (res);
413
414 if (GNUNET_SYSERR ==
415 GNUNET_PQ_run_sql (db,
416 db->load_path))
417 {
418 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
419 "Failed to load SQL statements from `%s*'\n",
420 db->load_path);
421 PQfinish (db->conn);
422 db->conn = NULL;
423 return;
424 }
425 }
426 if ( (NULL != db->es) &&
427 (GNUNET_OK !=
428 GNUNET_PQ_exec_statements (db,
429 db->es)) )
430 {
431 PQfinish (db->conn);
432 db->conn = NULL;
433 return;
434 }
435 if ( (NULL != db->ps) &&
436 (GNUNET_OK !=
437 GNUNET_PQ_prepare_statements (db,
438 db->ps)) )
439 {
440 PQfinish (db->conn);
441 db->conn = NULL;
442 return;
443 }
444 GNUNET_PQ_event_reconnect_ (db,
445 PQsocket (db->conn));
446}
447
448
449struct GNUNET_PQ_Context *
450GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
451 const char *section,
452 const char *load_path_suffix,
453 const struct GNUNET_PQ_ExecuteStatement *es,
454 const struct GNUNET_PQ_PreparedStatement *ps)
455{
456 return GNUNET_PQ_connect_with_cfg2 (cfg,
457 section,
458 load_path_suffix,
459 es,
460 ps,
461 GNUNET_PQ_FLAG_NONE);
462}
463
464
465struct GNUNET_PQ_Context *
466GNUNET_PQ_connect_with_cfg2 (const struct GNUNET_CONFIGURATION_Handle *cfg,
467 const char *section,
468 const char *load_path_suffix,
469 const struct GNUNET_PQ_ExecuteStatement *es,
470 const struct GNUNET_PQ_PreparedStatement *ps,
471 enum GNUNET_PQ_Options flags)
472{
473 struct GNUNET_PQ_Context *db;
474 char *conninfo;
475 char *load_path;
476 char *sp;
477
478 if (GNUNET_OK !=
479 GNUNET_CONFIGURATION_get_value_string (cfg,
480 section,
481 "CONFIG",
482 &conninfo))
483 conninfo = NULL;
484 load_path = NULL;
485 sp = NULL;
486 if ( (NULL != load_path_suffix) &&
487 (GNUNET_OK ==
488 GNUNET_CONFIGURATION_get_value_filename (cfg,
489 section,
490 "SQL_DIR",
491 &sp)) )
492 GNUNET_asprintf (&load_path,
493 "%s%s",
494 sp,
495 load_path_suffix);
496 db = GNUNET_PQ_connect2 (conninfo == NULL ? "" : conninfo,
497 load_path,
498 es,
499 ps,
500 flags);
501 GNUNET_free (load_path);
502 GNUNET_free (sp);
503 GNUNET_free (conninfo);
504 return db;
505}
506
507
508void
509GNUNET_PQ_disconnect (struct GNUNET_PQ_Context *db)
510{
511 if (NULL == db)
512 return;
513 GNUNET_assert (0 ==
514 GNUNET_CONTAINER_multishortmap_size (db->channel_map));
515 GNUNET_CONTAINER_multishortmap_destroy (db->channel_map);
516 GNUNET_free (db->es);
517 GNUNET_free (db->ps);
518 GNUNET_free (db->load_path);
519 GNUNET_free (db->config_str);
520 PQfinish (db->conn);
521 GNUNET_free (db);
522}
523
524
525/* end of pq/pq_connect.c */