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 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 
28 
37 static void
39  const PGresult *res)
40 {
41  /* do nothing, intentionally */
42  (void) arg;
43  (void) res;
44 }
45 
46 
54 static void
56  const char *message)
57 {
58  (void) arg;
60  "pq",
61  "%s",
62  message);
63 }
64 
65 
88 struct GNUNET_PQ_Context *
90  const char *load_path,
91  const struct GNUNET_PQ_ExecuteStatement *es,
92  const struct GNUNET_PQ_PreparedStatement *ps)
93 {
94  struct GNUNET_PQ_Context *db;
95  unsigned int elen = 0;
96  unsigned int plen = 0;
97 
98  if (NULL != es)
99  while (NULL != es[elen].sql)
100  elen++;
101  if (NULL != ps)
102  while (NULL != ps[plen].name)
103  plen++;
104 
105  db = GNUNET_new (struct GNUNET_PQ_Context);
106  db->config_str = GNUNET_strdup (config_str);
107  if (NULL != load_path)
108  db->load_path = GNUNET_strdup (load_path);
109  if (0 != elen)
110  {
111  db->es = GNUNET_new_array (elen + 1,
113  memcpy (db->es,
114  es,
115  elen * sizeof (struct GNUNET_PQ_ExecuteStatement));
116  }
117  if (0 != plen)
118  {
119  db->ps = GNUNET_new_array (plen + 1,
121  memcpy (db->ps,
122  ps,
123  plen * sizeof (struct GNUNET_PQ_PreparedStatement));
124  }
125  GNUNET_PQ_reconnect (db);
126  if (NULL == db->conn)
127  {
128  GNUNET_free (db->load_path);
129  GNUNET_free (db->config_str);
130  GNUNET_free (db);
131  return NULL;
132  }
133  return db;
134 }
135 
136 
145 static int
147  const char *load_path,
148  unsigned int i)
149 {
150  struct GNUNET_OS_Process *psql;
152  unsigned long code;
153  size_t slen = strlen (load_path) + 10;
154  char buf[slen];
155 
156  GNUNET_snprintf (buf,
157  sizeof (buf),
158  "%s%04u.sql",
159  load_path,
160  i);
162  "Applying SQL file `%s' on database %s\n",
163  buf,
164  db->config_str);
166  NULL,
167  NULL,
168  NULL,
169  "psql",
170  "psql",
171  db->config_str,
172  "-f",
173  buf,
174  "-q",
175  "--set",
176  "ON_ERROR_STOP=1",
177  NULL);
178  if (NULL == psql)
179  {
181  "exec",
182  "psql");
183  return GNUNET_SYSERR;
184  }
187  &type,
188  &code));
190  if ( (GNUNET_OS_PROCESS_EXITED != type) ||
191  (0 != code) )
192  {
194  "Could not run PSQL on file %s: %d\n",
195  buf,
196  (int) code);
197  return GNUNET_SYSERR;
198  }
199  return GNUNET_OK;
200 }
201 
202 
212 int
214  const char *load_path)
215 {
216  const char *load_path_suffix;
217  size_t slen = strlen (load_path) + 10;
218 
219  load_path_suffix = strrchr (load_path, '/');
220  if (NULL == load_path_suffix)
221  {
222  GNUNET_break (0);
223  return GNUNET_SYSERR;
224  }
225  load_path_suffix++; /* skip '/' */
227  "Loading SQL resources from `%s'\n",
228  load_path);
229  for (unsigned int i = 1; i<10000; i++)
230  {
231  enum GNUNET_DB_QueryStatus qs;
232  {
233  char buf[slen];
234 
235  /* First, check patch actually exists */
236  GNUNET_snprintf (buf,
237  sizeof (buf),
238  "%s%04u.sql",
239  load_path,
240  i);
241  if (GNUNET_YES !=
242  GNUNET_DISK_file_test (buf))
243  return GNUNET_OK; /* We are done */
244  }
245 
246  /* Second, check with DB versioning schema if this patch was already applied,
247  if so, skip it. */
248  {
249  char patch_name[slen];
250 
251  GNUNET_snprintf (patch_name,
252  sizeof (patch_name),
253  "%s%04u",
254  load_path_suffix,
255  i);
256  {
257  char *applied_by;
258  struct GNUNET_PQ_QueryParam params[] = {
259  GNUNET_PQ_query_param_string (patch_name),
261  };
262  struct GNUNET_PQ_ResultSpec rs[] = {
263  GNUNET_PQ_result_spec_string ("applied_by",
264  &applied_by),
266  };
267 
269  "gnunet_pq_check_patch",
270  params,
271  rs);
273  {
275  "Database version %s already applied by %s, skipping\n",
276  patch_name,
277  applied_by);
279  }
280  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
281  {
282  GNUNET_break (0);
283  return GNUNET_SYSERR;
284  }
285  }
286  }
288  continue; /* patch already applied, skip it */
289 
290  /* patch not yet applied, run it! */
291  {
292  int ret;
293 
294  ret = apply_patch (db,
295  load_path,
296  i);
297  if (GNUNET_NO == ret)
298  break;
299  if (GNUNET_SYSERR == ret)
300  return GNUNET_SYSERR;
301  }
302  }
303  return GNUNET_OK;
304 }
305 
306 
312 void
314 {
315  if (CONNECTION_BAD != PQstatus (db->conn))
316  return;
317  GNUNET_PQ_reconnect (db);
318 }
319 
320 
326 void
328 {
329  if (NULL != db->conn)
330  PQfinish (db->conn);
331  db->conn = PQconnectdb (db->config_str);
332  if ( (NULL == db->conn) ||
333  (CONNECTION_OK != PQstatus (db->conn)) )
334  {
336  "pq",
337  "Database connection to '%s' failed: %s\n",
338  db->config_str,
339  (NULL != db->conn) ?
340  PQerrorMessage (db->conn)
341  : "PQconnectdb returned NULL");
342  if (NULL != db->conn)
343  {
344  PQfinish (db->conn);
345  db->conn = NULL;
346  }
347  return;
348  }
349  PQsetNoticeReceiver (db->conn,
351  db);
352  PQsetNoticeProcessor (db->conn,
354  db);
355  if (NULL != db->load_path)
356  {
357  PGresult *res;
358 
359  res = PQprepare (db->conn,
360  "gnunet_pq_check_patch",
361  "SELECT"
362  " applied_by"
363  " FROM _v.patches"
364  " WHERE patch_name = $1"
365  " LIMIT 1",
366  1,
367  NULL);
368  if (PGRES_COMMAND_OK != PQresultStatus (res))
369  {
370  int ret;
371 
372  PQclear (res);
374  "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet, loading patch level 0000!\n");
375  ret = apply_patch (db,
376  db->load_path,
377  0);
378  if (GNUNET_NO == ret)
379  {
381  "Failed to find SQL file to load database versioning logic\n");
382  PQfinish (db->conn);
383  db->conn = NULL;
384  return;
385  }
386  if (GNUNET_SYSERR == ret)
387  {
389  "Failed to run SQL logic to setup database versioning logic\n");
390  PQfinish (db->conn);
391  db->conn = NULL;
392  return;
393  }
394  /* try again to prepare our statement! */
395  res = PQprepare (db->conn,
396  "gnunet_pq_check_patch",
397  "SELECT"
398  " applied_by"
399  " FROM _v.patches"
400  " WHERE patch_name = $1"
401  " LIMIT 1",
402  1,
403  NULL);
404  if (PGRES_COMMAND_OK != PQresultStatus (res))
405  {
407  "Failed to run SQL logic to setup database versioning logic: %s/%s\n",
408  PQresultErrorMessage (res),
409  PQerrorMessage (db->conn));
410  PQclear (res);
411  PQfinish (db->conn);
412  db->conn = NULL;
413  return;
414  }
415  }
416  PQclear (res);
417 
418  if (GNUNET_SYSERR ==
419  GNUNET_PQ_run_sql (db,
420  db->load_path))
421  {
423  "Failed to load SQL statements from `%s*'\n",
424  db->load_path);
425  PQfinish (db->conn);
426  db->conn = NULL;
427  return;
428  }
429  }
430  if ( (NULL != db->es) &&
431  (GNUNET_OK !=
433  db->es)) )
434  {
435  PQfinish (db->conn);
436  db->conn = NULL;
437  return;
438  }
439  if ( (NULL != db->ps) &&
440  (GNUNET_OK !=
442  db->ps)) )
443  {
444  PQfinish (db->conn);
445  db->conn = NULL;
446  return;
447  }
448 }
449 
450 
468 struct GNUNET_PQ_Context *
470  const char *section,
471  const char *load_path_suffix,
472  const struct GNUNET_PQ_ExecuteStatement *es,
473  const struct GNUNET_PQ_PreparedStatement *ps)
474 {
475  struct GNUNET_PQ_Context *db;
476  char *conninfo;
477  char *load_path;
478  char *sp;
479 
480  if (GNUNET_OK !=
482  section,
483  "CONFIG",
484  &conninfo))
485  conninfo = NULL;
486  load_path = NULL;
487  sp = NULL;
488  if ( (NULL != load_path_suffix) &&
489  (GNUNET_OK ==
491  section,
492  "SQL_DIR",
493  &sp)) )
494  GNUNET_asprintf (&load_path,
495  "%s%s",
496  sp,
497  load_path_suffix);
498  db = GNUNET_PQ_connect (conninfo == NULL ? "" : conninfo,
499  load_path,
500  es,
501  ps);
502  GNUNET_free (load_path);
503  GNUNET_free (sp);
504  GNUNET_free (conninfo);
505  return db;
506 }
507 
508 
515 void
517 {
518  GNUNET_free (db->es);
519  GNUNET_free (db->ps);
520  GNUNET_free (db->load_path);
521  GNUNET_free (db->config_str);
522  PQfinish (db->conn);
523  GNUNET_free (db);
524 }
525 
526 
527 /* end of pq/pq_connect.c */
int 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:544
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:516
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
void GNUNET_PQ_reconnect(struct GNUNET_PQ_Context *db)
Reinitialize the database db.
Definition: pq_connect.c:327
static const struct GNUNET_CONFIGURATION_Handle * cfg
Configuration we are using.
Definition: gnunet-abd.c:36
struct GNUNET_PQ_PreparedStatement * ps
Prepared statements.
Definition: pq.h:49
void GNUNET_OS_process_destroy(struct GNUNET_OS_Process *proc)
Cleans up process structure contents (OS-dependent) and deallocates it.
Definition: os_priority.c:287
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_string(const char *ptr)
Generate query parameter for a string.
char * load_path
Path to load SQL files from.
Definition: pq.h:59
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
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().
struct GNUNET_PQ_ExecuteStatement * es
Statements to execute upon connection.
Definition: pq.h:44
static int 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:146
Description of a DB result cell.
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
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
#define GNUNET_new(type)
Allocate a struct or union of the given type.
A hard error occurred, retrying will not help.
Definition: gnunet_db_lib.h:39
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
static char * section
Name of the section.
Definition: gnunet-config.c:33
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.
PGconn * conn
Actual connection.
Definition: pq.h:39
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:89
shared internal data structures of libgnunetpq
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_string(const char *name, char **dst)
0-terminated string expected.
#define GNUNET_PQ_result_spec_end
End of result parameter specification.
#define GNUNET_PQ_query_param_end
End of query parameter specification.
Definition: gnunet_pq_lib.h:97
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_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
char * config_str
Configuration to use to connect to the DB.
Definition: pq.h:54
#define GNUNET_log_strerror_file(level, cmd, filename)
Log an error message at log-level &#39;level&#39; that indicates a failure of the command &#39;cmd&#39; with the mess...
No standard streams should be inherited.
Definition: gnunet_os_lib.h:73
GNUNET_OS_ProcessStatusType
Process status types.
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
static char buf[2048]
#define GNUNET_new_array(n, type)
Allocate a size n array with structs or unions of the given type.
The process exited with a return code.
static int res
int int GNUNET_asprintf(char **buf, const char *format,...) __attribute__((format(printf
Like asprintf, just portable.
int 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.
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:469
The transaction succeeded, and yielded one result.
Definition: gnunet_db_lib.h:58
static struct GNUNET_FS_DirectoryBuilder * db
Definition: gnunet-search.c:41
void GNUNET_PQ_reconnect_if_down(struct GNUNET_PQ_Context *db)
Reinitialize the database db if the connection is down.
Definition: pq_connect.c:313
configuration data
Definition: configuration.c:84
const char * name
Description of a DB query parameter.
Definition: gnunet_pq_lib.h:64
#define GNUNET_log(kind,...)
int 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_TESTBED_UnderlayLinkModelType type
the type of this model
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:38
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:55
GNUNET_DB_QueryStatus
Status code returned from functions running database commands.
Definition: gnunet_db_lib.h:34
#define GNUNET_log_from(kind, comp,...)
Handle to Postgres database.
Definition: pq.h:34
int 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:213
#define GNUNET_free(ptr)
Wrapper around free.
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