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