GNUnet  0.11.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  {
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 
142 static enum GNUNET_GenericReturnValue
144  const char *load_path,
145  unsigned int i)
146 {
147  struct GNUNET_OS_Process *psql;
149  unsigned long code;
150  size_t slen = strlen (load_path) + 10;
151  char buf[slen];
152 
154  sizeof (buf),
155  "%s%04u.sql",
156  load_path,
157  i);
159  "Applying SQL file `%s' on database %s\n",
160  buf,
161  db->config_str);
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  {
178  "exec",
179  "psql");
180  return GNUNET_SYSERR;
181  }
184  &type,
185  &code));
187  if ( (GNUNET_OS_PROCESS_EXITED != type) ||
188  (0 != code) )
189  {
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 
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 '/' */
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 */
225  sizeof (buf),
226  "%s%04u.sql",
227  load_path,
228  i);
229  if (GNUNET_YES !=
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),
245  };
246  struct GNUNET_PQ_ResultSpec rs[] = {
247  GNUNET_PQ_result_spec_string ("applied_by",
248  &applied_by),
250  };
251 
253  "gnunet_pq_check_patch",
254  params,
255  rs);
257  {
259  "Database version %s already applied by %s, skipping\n",
260  patch_name,
261  applied_by);
263  }
264  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
265  {
266  GNUNET_break (0);
267  return GNUNET_SYSERR;
268  }
269  }
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! */
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! */
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 
299 void
301 {
302  if (1 ==
303  PQconsumeInput (db->conn))
304  return;
305  if (CONNECTION_BAD != PQstatus (db->conn))
306  return;
308 }
309 
310 
311 void
313 {
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  {
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,
338  db);
339  PQsetNoticeProcessor (db->conn,
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  {
358 
359  PQclear (res);
360  if (0 != (db->flags & GNUNET_PQ_FLAG_DROP))
361  {
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  }
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  {
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  {
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  {
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 ==
415  db->load_path))
416  {
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 !=
428  db->es)) )
429  {
430  PQfinish (db->conn);
431  db->conn = NULL;
432  return;
433  }
434  if ( (NULL != db->ps) &&
435  (GNUNET_OK !=
437  db->ps)) )
438  {
439  PQfinish (db->conn);
440  db->conn = NULL;
441  return;
442  }
444  PQsocket (db->conn));
445 }
446 
447 
448 struct GNUNET_PQ_Context *
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 {
456  section,
457  load_path_suffix,
458  es,
459  ps,
461 }
462 
463 
464 struct GNUNET_PQ_Context *
466  const char *section,
467  const char *load_path_suffix,
468  const struct GNUNET_PQ_ExecuteStatement *es,
469  const struct GNUNET_PQ_PreparedStatement *ps,
471 {
472  struct GNUNET_PQ_Context *db;
473  char *conninfo;
474  char *load_path;
475  char *sp;
476 
477  if (GNUNET_OK !=
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 ==
488  section,
489  "SQL_DIR",
490  &sp)) )
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);
501  GNUNET_free (sp);
502  GNUNET_free (conninfo);
503  return db;
504 }
505 
506 
507 void
509 {
510  if (NULL == db)
511  return;
512  GNUNET_assert (0 ==
513  GNUNET_CONTAINER_multishortmap_size (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 */
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:41
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
int 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
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
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:117
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:383
void GNUNET_PQ_reconnect(struct GNUNET_PQ_Context *db)
Reinitialize the database db.
Definition: pq_connect.c:312
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:201
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:300
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:508
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:449
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:143
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:465
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