GNUnet  0.16.x
pq_connect.c
Go to the documentation of this file.
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  */
25 #include "platform.h"
26 #include "pq.h"
27 #include <pthread.h>
28 
29 
38 static void
40  const PGresult *res)
41 {
42  /* do nothing, intentionally */
43  (void) arg;
44  (void) res;
45 }
46 
47 
55 static void
57  const char *message)
58 {
59  (void) arg;
61  "pq",
62  "%s",
63  message);
64 }
65 
66 
67 struct GNUNET_PQ_Context *
69  const char *load_path,
70  const struct GNUNET_PQ_ExecuteStatement *es,
71  const struct GNUNET_PQ_PreparedStatement *ps)
72 {
74  load_path,
75  es,
76  ps,
78 }
79 
80 
81 struct GNUNET_PQ_Context *
83  const char *load_path,
84  const struct GNUNET_PQ_ExecuteStatement *es,
85  const struct GNUNET_PQ_PreparedStatement *ps,
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,
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,
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);
123  if (NULL == db->conn)
124  {
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 
143 static enum GNUNET_GenericReturnValue
145  const char *load_path,
146  unsigned int i)
147 {
148  struct GNUNET_OS_Process *psql;
150  unsigned long code;
151  size_t slen = strlen (load_path) + 10;
152  char buf[slen];
153 
155  sizeof (buf),
156  "%s%04u.sql",
157  load_path,
158  i);
160  "Applying SQL file `%s' on database %s\n",
161  buf,
162  db->config_str);
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  {
179  "exec",
180  "psql");
181  return GNUNET_SYSERR;
182  }
185  &type,
186  &code));
188  if ( (GNUNET_OS_PROCESS_EXITED != type) ||
189  (0 != code) )
190  {
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 
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 '/' */
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 */
226  sizeof (buf),
227  "%s%04u.sql",
228  load_path,
229  i);
230  if (GNUNET_YES !=
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),
246  };
247  struct GNUNET_PQ_ResultSpec rs[] = {
248  GNUNET_PQ_result_spec_string ("applied_by",
249  &applied_by),
251  };
252 
254  "gnunet_pq_check_patch",
255  params,
256  rs);
258  {
260  "Database version %s already applied by %s, skipping\n",
261  patch_name,
262  applied_by);
264  }
265  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
266  {
267  GNUNET_break (0);
268  return GNUNET_SYSERR;
269  }
270  }
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! */
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! */
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 
300 void
302 {
303  if (1 ==
304  PQconsumeInput (db->conn))
305  return;
306  if (CONNECTION_BAD != PQstatus (db->conn))
307  return;
309 }
310 
311 
312 void
314 {
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  {
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,
339  db);
340  PQsetNoticeProcessor (db->conn,
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  {
359 
360  PQclear (res);
361  if (0 != (db->flags & GNUNET_PQ_FLAG_DROP))
362  {
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  }
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  {
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  {
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  {
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 ==
416  db->load_path))
417  {
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 !=
429  db->es)) )
430  {
431  PQfinish (db->conn);
432  db->conn = NULL;
433  return;
434  }
435  if ( (NULL != db->ps) &&
436  (GNUNET_OK !=
438  db->ps)) )
439  {
440  PQfinish (db->conn);
441  db->conn = NULL;
442  return;
443  }
445  PQsocket (db->conn));
446 }
447 
448 
449 struct GNUNET_PQ_Context *
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 {
457  section,
458  load_path_suffix,
459  es,
460  ps,
462 }
463 
464 
465 struct GNUNET_PQ_Context *
467  const char *section,
468  const char *load_path_suffix,
469  const struct GNUNET_PQ_ExecuteStatement *es,
470  const struct GNUNET_PQ_PreparedStatement *ps,
472 {
473  struct GNUNET_PQ_Context *db;
474  char *conninfo;
475  char *load_path;
476  char *sp;
477 
478  if (GNUNET_OK !=
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 ==
489  section,
490  "SQL_DIR",
491  &sp)) )
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);
502  GNUNET_free (sp);
503  GNUNET_free (conninfo);
504  return db;
505 }
506 
507 
508 void
510 {
511  if (NULL == db)
512  return;
513  GNUNET_assert (0 ==
514  GNUNET_CONTAINER_multishortmap_size (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 */
static const struct GNUNET_CONFIGURATION_Handle * cfg
Configuration we are using.
Definition: gnunet-abd.c:36
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
static int res
static struct GNUNET_FS_DirectoryBuilder * db
Definition: gnunet-search.c:94
static char buf[2048]
#define GNUNET_log(kind,...)
#define GNUNET_log_from(kind, comp,...)
GNUNET_GenericReturnValue
Named constants for return values.
Definition: gnunet_common.h:92
@ GNUNET_OK
Definition: gnunet_common.h:95
@ GNUNET_YES
Definition: gnunet_common.h:97
@ GNUNET_NO
Definition: gnunet_common.h:94
@ GNUNET_SYSERR
Definition: gnunet_common.h:93
GNUNET_DB_QueryStatus
Status code returned from functions running database commands.
Definition: gnunet_db_lib.h:36
@ GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
The transaction succeeded, and yielded one result.
Definition: gnunet_db_lib.h:59
@ GNUNET_DB_STATUS_HARD_ERROR
A hard error occurred, retrying will not help.
Definition: gnunet_db_lib.h:40
enum GNUNET_DB_QueryStatus GNUNET_PQ_eval_prepared_singleton_select(struct GNUNET_PQ_Context *db, const char *statement_name, const struct GNUNET_PQ_QueryParam *params, struct GNUNET_PQ_ResultSpec *rs)
Execute a named prepared statement that is a SELECT statement which must return a single result in co...
Definition: pq_eval.c:258
enum GNUNET_GenericReturnValue GNUNET_PQ_exec_statements(struct GNUNET_PQ_Context *db, const struct GNUNET_PQ_ExecuteStatement *es)
Request execution of an array of statements es from Postgres.
Definition: pq_exec.c:76
enum GNUNET_GenericReturnValue GNUNET_PQ_prepare_statements(struct GNUNET_PQ_Context *db, const struct GNUNET_PQ_PreparedStatement *ps)
Request creation of prepared statements ps from Postgres.
Definition: pq_prepare.c:62
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_string(const char *name, char **dst)
0-terminated string expected.
#define GNUNET_PQ_query_param_end
End of query parameter specification.
Definition: gnunet_pq_lib.h:97
GNUNET_PQ_Options
Flags to control PQ operation.
@ GNUNET_PQ_FLAG_DROP
Dropping database.
@ GNUNET_PQ_FLAG_CHECK_CURRENT
Check database version is current.
@ GNUNET_PQ_FLAG_NONE
Traditional default.
void GNUNET_PQ_cleanup_result(struct GNUNET_PQ_ResultSpec *rs)
Free all memory that was allocated in rs during GNUNET_PQ_extract_result().
Definition: pq.c:122
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_string(const char *ptr)
Generate query parameter for a string.
#define GNUNET_PQ_result_spec_end
End of result parameter specification.
enum GNUNET_GenericReturnValue GNUNET_CONFIGURATION_get_value_filename(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be the name of a file or directory.
enum GNUNET_GenericReturnValue GNUNET_CONFIGURATION_get_value_string(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be a string.
enum GNUNET_GenericReturnValue GNUNET_DISK_file_test(const char *fil)
Check that fil corresponds to a filename (of a file that exists and that is not a directory).
Definition: disk.c:482
struct GNUNET_CONTAINER_MultiShortmap * GNUNET_CONTAINER_multishortmap_create(unsigned int len, int do_not_copy_keys)
Create a multi peer map (hash map for public keys of peers).
void GNUNET_CONTAINER_multishortmap_destroy(struct GNUNET_CONTAINER_MultiShortmap *map)
Destroy a hash map.
unsigned int GNUNET_CONTAINER_multishortmap_size(const struct GNUNET_CONTAINER_MultiShortmap *map)
Get the number of key-value pairs in the map.
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
#define GNUNET_log_strerror_file(level, cmd, filename)
Log an error message at log-level 'level' that indicates a failure of the command 'cmd' with the mess...
@ GNUNET_ERROR_TYPE_WARNING
@ GNUNET_ERROR_TYPE_ERROR
@ GNUNET_ERROR_TYPE_DEBUG
@ GNUNET_ERROR_TYPE_INFO
int int GNUNET_asprintf(char **buf, const char *format,...) __attribute__((format(printf
Like asprintf, just portable.
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
int GNUNET_snprintf(char *buf, size_t size, const char *format,...) __attribute__((format(printf
Like snprintf, just aborts if the buffer is of insufficient size.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_new_array(n, type)
Allocate a size n array with structs or unions of the given type.
#define GNUNET_free(ptr)
Wrapper around free.
struct GNUNET_OS_Process * GNUNET_OS_start_process(enum GNUNET_OS_InheritStdioFlags std_inheritance, struct GNUNET_DISK_PipeHandle *pipe_stdin, struct GNUNET_DISK_PipeHandle *pipe_stdout, struct GNUNET_DISK_PipeHandle *pipe_stderr, const char *filename,...)
Start a process.
Definition: os_priority.c:678
int GNUNET_OS_process_wait_status(struct GNUNET_OS_Process *proc, enum GNUNET_OS_ProcessStatusType *type, unsigned long *code)
Retrieve the status of a process, waiting on it if dead.
Definition: os_priority.c:964
GNUNET_OS_ProcessStatusType
Process status types.
void GNUNET_OS_process_destroy(struct GNUNET_OS_Process *proc)
Cleans up process structure contents (OS-dependent) and deallocates it.
Definition: os_priority.c:286
@ GNUNET_OS_INHERIT_STD_ERR
When this flag is set, the child process will inherit stderr of the parent.
Definition: gnunet_os_lib.h:91
@ GNUNET_OS_PROCESS_EXITED
The process exited with a return code.
const char * name
shared internal data structures of libgnunetpq
void GNUNET_PQ_event_reconnect_(struct GNUNET_PQ_Context *db, int fd)
Internal API.
Definition: pq_event.c:406
void GNUNET_PQ_reconnect(struct GNUNET_PQ_Context *db)
Reinitialize the database db.
Definition: pq_connect.c:313
enum GNUNET_GenericReturnValue GNUNET_PQ_run_sql(struct GNUNET_PQ_Context *db, const char *load_path)
Within the db context, run all the SQL files from the load_path from 0000-9999.sql (as long as the fi...
Definition: pq_connect.c:202
static void pq_notice_receiver_cb(void *arg, const PGresult *res)
Function called by libpq whenever it wants to log something.
Definition: pq_connect.c:39
void GNUNET_PQ_reconnect_if_down(struct GNUNET_PQ_Context *db)
Reinitialize the database db if the connection is down.
Definition: pq_connect.c:301
void GNUNET_PQ_disconnect(struct GNUNET_PQ_Context *db)
Disconnect from the database, destroying the prepared statements and releasing other associated resou...
Definition: pq_connect.c:509
struct GNUNET_PQ_Context * GNUNET_PQ_connect(const char *config_str, const char *load_path, const struct GNUNET_PQ_ExecuteStatement *es, const struct GNUNET_PQ_PreparedStatement *ps)
Create a connection to the Postgres database using config_str for the configuration.
Definition: pq_connect.c:68
struct GNUNET_PQ_Context * GNUNET_PQ_connect_with_cfg(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *load_path_suffix, const struct GNUNET_PQ_ExecuteStatement *es, const struct GNUNET_PQ_PreparedStatement *ps)
Connect to a postgres database using the configuration option "CONFIG" in section.
Definition: pq_connect.c:450
struct GNUNET_PQ_Context * GNUNET_PQ_connect2(const char *config_str, const char *load_path, const struct GNUNET_PQ_ExecuteStatement *es, const struct GNUNET_PQ_PreparedStatement *ps, enum GNUNET_PQ_Options flags)
Create a connection to the Postgres database using config_str for the configuration.
Definition: pq_connect.c:82
static enum GNUNET_GenericReturnValue apply_patch(struct GNUNET_PQ_Context *db, const char *load_path, unsigned int i)
Apply patch number from path load_path.
Definition: pq_connect.c:144
struct GNUNET_PQ_Context * GNUNET_PQ_connect_with_cfg2(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *load_path_suffix, const struct GNUNET_PQ_ExecuteStatement *es, const struct GNUNET_PQ_PreparedStatement *ps, enum GNUNET_PQ_Options flags)
Connect to a postgres database using the configuration option "CONFIG" in section.
Definition: pq_connect.c:466
static void pq_notice_processor_cb(void *arg, const char *message)
Function called by libpq whenever it wants to log something.
Definition: pq_connect.c:56
Handle to Postgres database.
Definition: pq.h:36
struct GNUNET_PQ_ExecuteStatement * es
Statements to execute upon connection.
Definition: pq.h:45
enum GNUNET_PQ_Options flags
Flags controlling the connection.
Definition: pq.h:80
char * load_path
Path to load SQL files from.
Definition: pq.h:60
struct GNUNET_PQ_PreparedStatement * ps
Prepared statements.
Definition: pq.h:50
char * config_str
Configuration to use to connect to the DB.
Definition: pq.h:55
Information needed to run a list of SQL statements using GNUNET_PQ_exec_statements().
Information needed to prepare a list of SQL statements using GNUNET_PQ_prepare_statements().
Description of a DB query parameter.
Definition: gnunet_pq_lib.h:65
Description of a DB result cell.
enum GNUNET_TESTBED_UnderlayLinkModelType type
the type of this model