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  {
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);
167  NULL,
168  NULL,
169  NULL,
170  "psql",
171  "psql",
172  db->config_str,
173  "-f",
174  buf,
175  "-q",
176  NULL);
177  if (NULL == psql)
178  {
180  "exec",
181  "psql");
182  return GNUNET_SYSERR;
183  }
186  &type,
187  &code));
189  if ( (GNUNET_OS_PROCESS_EXITED != type) ||
190  (0 != code) )
191  {
193  "Could not run PSQL on file %s: %d\n",
194  buf,
195  (int) code);
196  return GNUNET_SYSERR;
197  }
198  return GNUNET_OK;
199 }
200 
201 
211 int
213  const char *load_path)
214 {
215  const char *load_path_suffix;
216  size_t slen = strlen (load_path) + 10;
217 
218  load_path_suffix = strrchr (load_path, '/');
219  if (NULL == load_path_suffix)
220  {
221  GNUNET_break (0);
222  return GNUNET_SYSERR;
223  }
224  load_path_suffix++; /* skip '/' */
225  for (unsigned int i = 1; i<10000; i++)
226  {
227  enum GNUNET_DB_QueryStatus qs;
228  {
229  char buf[slen];
230 
231  /* First, check patch actually exists */
232  GNUNET_snprintf (buf,
233  sizeof (buf),
234  "%s%04u.sql",
235  load_path,
236  i);
237  if (GNUNET_YES !=
238  GNUNET_DISK_file_test (buf))
239  return GNUNET_OK; /* We are done */
240  }
241 
242  /* Second, check with DB versioning schema if this patch was already applied,
243  if so, skip it. */
244  {
245  char patch_name[slen];
246 
247  GNUNET_snprintf (patch_name,
248  sizeof (patch_name),
249  "%s%04u",
250  load_path_suffix,
251  i);
252  {
253  char *applied_by;
254  struct GNUNET_PQ_QueryParam params[] = {
255  GNUNET_PQ_query_param_string (patch_name),
257  };
258  struct GNUNET_PQ_ResultSpec rs[] = {
259  GNUNET_PQ_result_spec_string ("applied_by",
260  &applied_by),
262  };
263 
265  "gnunet_pq_check_patch",
266  params,
267  rs);
269  {
271  "Database version %s already applied by %s, skipping\n",
272  patch_name,
273  applied_by);
275  }
276  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
277  {
278  GNUNET_break (0);
279  return GNUNET_SYSERR;
280  }
281  }
282  }
284  continue; /* patch already applied, skip it */
285 
286  /* patch not yet applied, run it! */
287  {
288  int ret;
289 
290  ret = apply_patch (db,
291  load_path,
292  i);
293  if (GNUNET_NO == ret)
294  break;
295  if (GNUNET_SYSERR == ret)
296  return GNUNET_SYSERR;
297  }
298  }
299  return GNUNET_OK;
300 }
301 
302 
308 void
310 {
311  if (CONNECTION_BAD != PQstatus (db->conn))
312  return;
313  GNUNET_PQ_reconnect (db);
314 }
315 
316 
322 void
324 {
325  if (NULL != db->conn)
326  PQfinish (db->conn);
327  db->conn = PQconnectdb (db->config_str);
328  if ( (NULL == db->conn) ||
329  (CONNECTION_OK != PQstatus (db->conn)) )
330  {
332  "pq",
333  "Database connection to '%s' failed: %s\n",
334  db->config_str,
335  (NULL != db->conn) ?
336  PQerrorMessage (db->conn)
337  : "PQconnectdb returned NULL");
338  if (NULL != db->conn)
339  {
340  PQfinish (db->conn);
341  db->conn = NULL;
342  }
343  return;
344  }
345  PQsetNoticeReceiver (db->conn,
347  db);
348  PQsetNoticeProcessor (db->conn,
350  db);
351  if (NULL != db->load_path)
352  {
353  PGresult *res;
354 
355  res = PQprepare (db->conn,
356  "gnunet_pq_check_patch",
357  "SELECT"
358  " applied_by"
359  " FROM _v.patches"
360  " WHERE patch_name = $1"
361  " LIMIT 1",
362  1,
363  NULL);
364  if (PGRES_COMMAND_OK != PQresultStatus (res))
365  {
366  int ret;
367 
368  PQclear (res);
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 ==
415  GNUNET_PQ_run_sql (db,
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  }
444 }
445 
446 
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)
470 {
471  struct GNUNET_PQ_Context *db;
472  char *conninfo;
473  char *load_path;
474  char *sp;
475 
476  if (GNUNET_OK !=
478  section,
479  "CONFIG",
480  &conninfo))
481  conninfo = NULL;
482  load_path = NULL;
483  sp = NULL;
484  if ( (NULL != load_path_suffix) &&
485  (GNUNET_OK ==
487  section,
488  "SQL_DIR",
489  &sp)) )
490  GNUNET_asprintf (&load_path,
491  "%s%s",
492  sp,
493  load_path_suffix);
494  db = GNUNET_PQ_connect (conninfo == NULL ? "" : conninfo,
495  load_path,
496  es,
497  ps);
498  GNUNET_free_non_null (load_path);
500  GNUNET_free_non_null (conninfo);
501  return db;
502 }
503 
504 
511 void
513 {
514  GNUNET_free_non_null (db->es);
515  GNUNET_free_non_null (db->ps);
518  PQfinish (db->conn);
519  GNUNET_free (db);
520 }
521 
522 
523 /* 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:512
void GNUNET_PQ_reconnect(struct GNUNET_PQ_Context *db)
Reinitialize the database db.
Definition: pq_connect.c:323
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
int GNUNET_snprintf(char *buf, size_t size, const char *format,...)
Like snprintf, just aborts if the buffer is of insufficient size.
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().
struct GNUNET_OS_Process * GNUNET_OS_start_process(int pipe_control, 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:687
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_NO
Definition: gnunet_common.h:86
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:83
#define GNUNET_free_non_null(ptr)
Free the memory pointed to by ptr if ptr is not NULL.
#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
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:96
int GNUNET_asprintf(char **buf, const char *format,...)
Like asprintf, just portable.
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:981
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 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:465
#define GNUNET_SYSERR
Definition: gnunet_common.h:84
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:309
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
#define GNUNET_YES
Definition: gnunet_common.h:85
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:212
#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:130