aboutsummaryrefslogtreecommitdiff
path: root/src/pq
diff options
context:
space:
mode:
Diffstat (limited to 'src/pq')
-rw-r--r--src/pq/.gitignore1
-rw-r--r--src/pq/Makefile.am42
-rw-r--r--src/pq/pq.c190
-rw-r--r--src/pq/pq.h96
-rw-r--r--src/pq/pq_connect.c524
-rw-r--r--src/pq/pq_eval.c307
-rw-r--r--src/pq/pq_event.c558
-rw-r--r--src/pq/pq_exec.c117
-rw-r--r--src/pq/pq_prepare.c122
-rw-r--r--src/pq/pq_query_helper.c520
-rw-r--r--src/pq/pq_result_helper.c907
-rw-r--r--src/pq/test_pq.c421
12 files changed, 0 insertions, 3805 deletions
diff --git a/src/pq/.gitignore b/src/pq/.gitignore
deleted file mode 100644
index 8de68ddc9..000000000
--- a/src/pq/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
1test_pq
diff --git a/src/pq/Makefile.am b/src/pq/Makefile.am
deleted file mode 100644
index cbb123cbb..000000000
--- a/src/pq/Makefile.am
+++ /dev/null
@@ -1,42 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS)
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage
6endif
7
8if HAVE_POSTGRESQL
9lib_LTLIBRARIES = libgnunetpq.la
10endif
11
12libgnunetpq_la_SOURCES = \
13 pq.c \
14 pq.h \
15 pq_connect.c \
16 pq_eval.c \
17 pq_event.c \
18 pq_exec.c \
19 pq_prepare.c \
20 pq_query_helper.c \
21 pq_result_helper.c
22libgnunetpq_la_LIBADD = -lpq \
23 $(top_builddir)/src/util/libgnunetutil.la
24libgnunetpq_la_LDFLAGS = \
25 $(POSTGRESQL_LDFLAGS) \
26 $(GN_LIB_LDFLAGS) \
27 -version-info 1:0:0
28
29if ENABLE_TEST_RUN
30TESTS = \
31 test_pq
32endif
33
34check_PROGRAMS= \
35 test_pq
36
37test_pq_SOURCES = \
38 test_pq.c
39test_pq_LDADD = \
40 libgnunetpq.la \
41 $(top_builddir)/src/util/libgnunetutil.la \
42 -lpq $(XLIB)
diff --git a/src/pq/pq.c b/src/pq/pq.c
deleted file mode 100644
index 130ff355f..000000000
--- a/src/pq/pq.c
+++ /dev/null
@@ -1,190 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2016, 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 */
20/**
21 * @file pq/pq.c
22 * @brief helper functions for libpq (PostGres) interactions
23 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
24 * @author Florian Dold
25 * @author Christian Grothoff
26 */
27#include "platform.h"
28#include "pq.h"
29
30
31PGresult *
32GNUNET_PQ_exec_prepared (struct GNUNET_PQ_Context *db,
33 const char *name,
34 const struct GNUNET_PQ_QueryParam *params)
35{
36 unsigned int len;
37
38 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
39 "Running prepared statement `%s' on %p\n",
40 name,
41 db);
42 /* count the number of parameters */
43 len = 0;
44 for (unsigned int i = 0; 0 != params[i].num_params; i++)
45 len += params[i].num_params;
46
47 /* new scope to allow stack allocation without alloca */
48 {
49 /* Scratch buffer for temporary storage */
50 void *scratch[len];
51 /* Parameter array we are building for the query */
52 void *param_values[len];
53 int param_lengths[len];
54 int param_formats[len];
55 unsigned int off;
56 /* How many entries in the scratch buffer are in use? */
57 unsigned int soff;
58 PGresult *res;
59 int ret;
60 ConnStatusType status;
61
62 off = 0;
63 soff = 0;
64 for (unsigned int i = 0; 0 != params[i].num_params; i++)
65 {
66 const struct GNUNET_PQ_QueryParam *x = &params[i];
67
68 ret = x->conv (x->conv_cls,
69 x->data,
70 x->size,
71 &param_values[off],
72 &param_lengths[off],
73 &param_formats[off],
74 x->num_params,
75 &scratch[soff],
76 len - soff);
77 if (ret < 0)
78 {
79 for (off = 0; off < soff; off++)
80 GNUNET_free (scratch[off]);
81 return NULL;
82 }
83 soff += ret;
84 off += x->num_params;
85 }
86 GNUNET_assert (off == len);
87 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
88 "pq",
89 "Executing prepared SQL statement `%s'\n",
90 name);
91 res = PQexecPrepared (db->conn,
92 name,
93 len,
94 (const char **) param_values,
95 param_lengths,
96 param_formats,
97 1);
98 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
99 "pq",
100 "Execution of prepared SQL statement `%s' finished (%d)\n",
101 name,
102 PGRES_COMMAND_OK == PQresultStatus (res));
103 if ( (PGRES_COMMAND_OK != PQresultStatus (res)) &&
104 (CONNECTION_OK != (status = PQstatus (db->conn))) )
105 {
106 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
107 "pq",
108 "Database disconnected on SQL statement `%s' (reconnecting)\n",
109 name);
110 GNUNET_PQ_reconnect (db);
111 res = NULL;
112 }
113
114 for (off = 0; off < soff; off++)
115 GNUNET_free (scratch[off]);
116 return res;
117 }
118}
119
120
121void
122GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs)
123{
124 for (unsigned int i = 0; NULL != rs[i].conv; i++)
125 if (NULL != rs[i].cleaner)
126 rs[i].cleaner (rs[i].cls,
127 rs[i].dst);
128}
129
130
131enum GNUNET_GenericReturnValue
132GNUNET_PQ_extract_result (PGresult *result,
133 struct GNUNET_PQ_ResultSpec *rs,
134 int row)
135{
136 unsigned int i;
137
138 if (NULL == result)
139 return GNUNET_SYSERR;
140 for (i = 0; NULL != rs[i].conv; i++)
141 {
142 struct GNUNET_PQ_ResultSpec *spec;
143 enum GNUNET_GenericReturnValue ret;
144
145 spec = &rs[i];
146 ret = spec->conv (spec->cls,
147 result,
148 row,
149 spec->fname,
150 &spec->dst_size,
151 spec->dst);
152 switch (ret)
153 {
154 case GNUNET_OK:
155 /* canonical case, continue below */
156 if (NULL != spec->is_null)
157 *spec->is_null = false;
158 break;
159 case GNUNET_NO:
160 if (spec->is_nullable)
161 {
162 if (NULL != spec->is_null)
163 *spec->is_null = true;
164 continue;
165 }
166 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
167 "NULL field encountered for `%s' where non-NULL was required\n",
168 spec->fname);
169 goto cleanup;
170 case GNUNET_SYSERR:
171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
172 "Failed to extract field `%s'\n",
173 spec->fname);
174 GNUNET_break (0);
175 goto cleanup;
176 }
177 if (NULL != spec->result_size)
178 *spec->result_size = spec->dst_size;
179 }
180 return GNUNET_OK;
181cleanup:
182 for (unsigned int j = 0; j < i; j++)
183 if (NULL != rs[j].cleaner)
184 rs[j].cleaner (rs[j].cls,
185 rs[j].dst);
186 return GNUNET_SYSERR;
187}
188
189
190/* end of pq/pq.c */
diff --git a/src/pq/pq.h b/src/pq/pq.h
deleted file mode 100644
index 354d85a9f..000000000
--- a/src/pq/pq.h
+++ /dev/null
@@ -1,96 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2019 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.h
22 * @brief shared internal data structures of libgnunetpq
23 * @author Christian Grothoff
24 */
25#ifndef PQ_H
26#define PQ_H
27
28#include "gnunet_util_lib.h"
29#include "gnunet_pq_lib.h"
30
31
32/**
33 * Handle to Postgres database.
34 */
35struct GNUNET_PQ_Context
36{
37 /**
38 * Actual connection.
39 */
40 PGconn *conn;
41
42 /**
43 * Statements to execute upon connection.
44 */
45 struct GNUNET_PQ_ExecuteStatement *es;
46
47 /**
48 * Prepared statements.
49 */
50 struct GNUNET_PQ_PreparedStatement *ps;
51
52 /**
53 * Configuration to use to connect to the DB.
54 */
55 char *config_str;
56
57 /**
58 * Path to load SQL files from.
59 */
60 char *load_path;
61
62 /**
63 * Map managing event subscriptions.
64 */
65 struct GNUNET_CONTAINER_MultiShortmap *channel_map;
66
67 /**
68 * Task responsible for processing events.
69 */
70 struct GNUNET_SCHEDULER_Task *event_task;
71
72 /**
73 * File descriptor wrapper for @e event_task.
74 */
75 struct GNUNET_NETWORK_Handle *rfd;
76
77 /**
78 * Flags controlling the connection.
79 */
80 enum GNUNET_PQ_Options flags;
81};
82
83
84/**
85 * Internal API. Reconnect should re-register notifications
86 * after a disconnect.
87 *
88 * @param db the DB handle
89 * @param fd socket to listen on
90 */
91void
92GNUNET_PQ_event_reconnect_ (struct GNUNET_PQ_Context *db,
93 int fd);
94
95
96#endif
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 */
diff --git a/src/pq/pq_eval.c b/src/pq/pq_eval.c
deleted file mode 100644
index 5f96ff884..000000000
--- a/src/pq/pq_eval.c
+++ /dev/null
@@ -1,307 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2019 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_eval.c
22 * @brief functions to execute SQL statements with arguments and/or results (PostGres)
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "pq.h"
27
28
29/**
30 * Error code returned by Postgres for deadlock.
31 */
32#define PQ_DIAG_SQLSTATE_DEADLOCK "40P01"
33
34/**
35 * Error code returned by Postgres for uniqueness violation.
36 */
37#define PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION "23505"
38
39/**
40 * Error code returned by Postgres on serialization failure.
41 */
42#define PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE "40001"
43
44
45/**
46 * Check the @a result's error code to see what happened.
47 * Also logs errors.
48 *
49 * @param db database to execute the statement with
50 * @param statement_name name of the statement that created @a result
51 * @param result result to check
52 * @return status code from the result, mapping PQ status
53 * codes to `enum GNUNET_DB_QueryStatus`. Never
54 * returns positive values as this function does
55 * not look at the result set.
56 * @deprecated (low level, let's see if we can do with just the high-level functions)
57 */
58enum GNUNET_DB_QueryStatus
59GNUNET_PQ_eval_result (struct GNUNET_PQ_Context *db,
60 const char *statement_name,
61 PGresult *result)
62{
63 ExecStatusType est;
64
65 if (NULL == result)
66 return GNUNET_DB_STATUS_SOFT_ERROR;
67 est = PQresultStatus (result);
68 if ((PGRES_COMMAND_OK != est) &&
69 (PGRES_TUPLES_OK != est))
70 {
71 const char *sqlstate;
72 ConnStatusType status;
73
74 if (CONNECTION_OK != (status = PQstatus (db->conn)))
75 {
76 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
77 "pq",
78 "Database connection failed during query `%s': %d (reconnecting)\n",
79 statement_name,
80 status);
81 GNUNET_PQ_reconnect (db);
82 return GNUNET_DB_STATUS_SOFT_ERROR;
83 }
84
85 sqlstate = PQresultErrorField (result,
86 PG_DIAG_SQLSTATE);
87 if (NULL == sqlstate)
88 {
89 /* very unexpected... */
90 GNUNET_break (0);
91 return GNUNET_DB_STATUS_HARD_ERROR;
92 }
93 if ((0 == strcmp (sqlstate,
94 PQ_DIAG_SQLSTATE_DEADLOCK)) ||
95 (0 == strcmp (sqlstate,
96 PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE)))
97 {
98 /* These two can be retried and have a fair chance of working
99 the next time */
100 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
101 "pq",
102 "Query `%s' failed with result: %s/%s/%s/%s/%s\n",
103 statement_name,
104 PQresultErrorField (result,
105 PG_DIAG_MESSAGE_PRIMARY),
106 PQresultErrorField (result,
107 PG_DIAG_MESSAGE_DETAIL),
108 PQresultErrorMessage (result),
109 PQresStatus (PQresultStatus (result)),
110 PQerrorMessage (db->conn));
111 return GNUNET_DB_STATUS_SOFT_ERROR;
112 }
113 if (0 == strcmp (sqlstate,
114 PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION))
115 {
116 /* Likely no need to retry, INSERT of "same" data. */
117 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
118 "pq",
119 "Query `%s' failed with unique violation: %s/%s/%s/%s/%s\n",
120 statement_name,
121 PQresultErrorField (result,
122 PG_DIAG_MESSAGE_PRIMARY),
123 PQresultErrorField (result,
124 PG_DIAG_MESSAGE_DETAIL),
125 PQresultErrorMessage (result),
126 PQresStatus (PQresultStatus (result)),
127 PQerrorMessage (db->conn));
128 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
129 }
130 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
131 "pq",
132 "Query `%s' failed with result: %s/%s/%s/%s/%s\n",
133 statement_name,
134 PQresultErrorField (result,
135 PG_DIAG_MESSAGE_PRIMARY),
136 PQresultErrorField (result,
137 PG_DIAG_MESSAGE_DETAIL),
138 PQresultErrorMessage (result),
139 PQresStatus (PQresultStatus (result)),
140 PQerrorMessage (db->conn));
141 return GNUNET_DB_STATUS_HARD_ERROR;
142 }
143 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
144}
145
146
147/**
148 * Execute a named prepared @a statement that is NOT a SELECT
149 * statement in @a connection using the given @a params. Returns the
150 * resulting session state.
151 *
152 * @param db database to execute the statement with
153 * @param statement_name name of the statement
154 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
155 * @return status code from the result, mapping PQ status
156 * codes to `enum GNUNET_DB_QueryStatus`. If the
157 * statement was a DELETE or UPDATE statement, the
158 * number of affected rows is returned.; if the
159 * statement was an INSERT statement, and no row
160 * was added due to a UNIQUE violation, we return
161 * zero; if INSERT was successful, we return one.
162 */
163enum GNUNET_DB_QueryStatus
164GNUNET_PQ_eval_prepared_non_select (struct GNUNET_PQ_Context *db,
165 const char *statement_name,
166 const struct GNUNET_PQ_QueryParam *params)
167{
168 PGresult *result;
169 enum GNUNET_DB_QueryStatus qs;
170
171 result = GNUNET_PQ_exec_prepared (db,
172 statement_name,
173 params);
174 if (NULL == result)
175 return GNUNET_DB_STATUS_SOFT_ERROR;
176 qs = GNUNET_PQ_eval_result (db,
177 statement_name,
178 result);
179 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
180 {
181 const char *tuples;
182
183 /* What an awful API, this function really does return a string */
184 tuples = PQcmdTuples (result);
185 if (NULL != tuples)
186 qs = strtol (tuples, NULL, 10);
187 }
188 PQclear (result);
189 return qs;
190}
191
192
193/**
194 * Execute a named prepared @a statement that is a SELECT statement
195 * which may return multiple results in @a connection using the given
196 * @a params. Call @a rh with the results. Returns the query
197 * status including the number of results given to @a rh (possibly zero).
198 * @a rh will not have been called if the return value is negative.
199 *
200 * @param db database to execute the statement with
201 * @param statement_name name of the statement
202 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
203 * @param rh function to call with the result set, NULL to ignore
204 * @param rh_cls closure to pass to @a rh
205 * @return status code from the result, mapping PQ status
206 * codes to `enum GNUNET_DB_QueryStatus`.
207 */
208enum GNUNET_DB_QueryStatus
209GNUNET_PQ_eval_prepared_multi_select (struct GNUNET_PQ_Context *db,
210 const char *statement_name,
211 const struct GNUNET_PQ_QueryParam *params,
212 GNUNET_PQ_PostgresResultHandler rh,
213 void *rh_cls)
214{
215 PGresult *result;
216 enum GNUNET_DB_QueryStatus qs;
217 unsigned int ret;
218
219 result = GNUNET_PQ_exec_prepared (db,
220 statement_name,
221 params);
222 if (NULL == result)
223 return GNUNET_DB_STATUS_SOFT_ERROR;
224 qs = GNUNET_PQ_eval_result (db,
225 statement_name,
226 result);
227 if (qs < 0)
228 {
229 PQclear (result);
230 return qs;
231 }
232 ret = PQntuples (result);
233 if (NULL != rh)
234 rh (rh_cls,
235 result,
236 ret);
237 PQclear (result);
238 return ret;
239}
240
241
242/**
243 * Execute a named prepared @a statement that is a SELECT statement
244 * which must return a single result in @a connection using the given
245 * @a params. Stores the result (if any) in @a rs, which the caller
246 * must then clean up using #GNUNET_PQ_cleanup_result() if the return
247 * value was #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT. Returns the
248 * resulting session status.
249 *
250 * @param db database to execute the statement with
251 * @param statement_name name of the statement
252 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
253 * @param[in,out] rs result specification to use for storing the result of the query
254 * @return status code from the result, mapping PQ status
255 * codes to `enum GNUNET_DB_QueryStatus`.
256 */
257enum GNUNET_DB_QueryStatus
258GNUNET_PQ_eval_prepared_singleton_select (struct GNUNET_PQ_Context *db,
259 const char *statement_name,
260 const struct
261 GNUNET_PQ_QueryParam *params,
262 struct GNUNET_PQ_ResultSpec *rs)
263{
264 PGresult *result;
265 enum GNUNET_DB_QueryStatus qs;
266 int ntuples;
267
268 result = GNUNET_PQ_exec_prepared (db,
269 statement_name,
270 params);
271 if (NULL == result)
272 return GNUNET_DB_STATUS_SOFT_ERROR;
273 qs = GNUNET_PQ_eval_result (db,
274 statement_name,
275 result);
276 if (qs < 0)
277 {
278 PQclear (result);
279 return qs;
280 }
281 ntuples = PQntuples (result);
282 if (0 == ntuples)
283 {
284 PQclear (result);
285 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
286 }
287 if (1 != ntuples)
288 {
289 /* more than one result, but there must be at most one */
290 GNUNET_break (0);
291 PQclear (result);
292 return GNUNET_DB_STATUS_HARD_ERROR;
293 }
294 if (GNUNET_OK !=
295 GNUNET_PQ_extract_result (result,
296 rs,
297 0))
298 {
299 PQclear (result);
300 return GNUNET_DB_STATUS_HARD_ERROR;
301 }
302 PQclear (result);
303 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
304}
305
306
307/* end of pq/pq_eval.c */
diff --git a/src/pq/pq_event.c b/src/pq/pq_event.c
deleted file mode 100644
index 98a28c317..000000000
--- a/src/pq/pq_event.c
+++ /dev/null
@@ -1,558 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 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_event.c
22 * @brief event notifications via Postgres
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "pq.h"
27#include <pthread.h>
28
29
30/**
31 * Handle for an active LISTENer to the database.
32 */
33struct GNUNET_DB_EventHandler
34{
35 /**
36 * Channel name.
37 */
38 struct GNUNET_ShortHashCode sh;
39
40 /**
41 * Function to call on events.
42 */
43 GNUNET_DB_EventCallback cb;
44
45 /**
46 * Closure for @e cb.
47 */
48 void *cb_cls;
49
50 /**
51 * Database context this event handler is with.
52 */
53 struct GNUNET_PQ_Context *db;
54
55 /**
56 * Task to run on timeout.
57 */
58 struct GNUNET_SCHEDULER_Task *timeout_task;
59};
60
61
62/**
63 * Convert @a es to a short hash.
64 *
65 * @param es spec to hash to an identifier
66 * @param[out] sh short hash to set
67 */
68static void
69es_to_sh (const struct GNUNET_DB_EventHeaderP *es,
70 struct GNUNET_ShortHashCode *sh)
71{
72 struct GNUNET_HashCode h_channel;
73
74 GNUNET_CRYPTO_hash (es,
75 ntohs (es->size),
76 &h_channel);
77 GNUNET_static_assert (sizeof (*sh) <= sizeof (h_channel));
78 memcpy (sh,
79 &h_channel,
80 sizeof (*sh));
81}
82
83
84/**
85 * Convert @a sh to a Postgres identifier.
86 *
87 * @param sh short hash to convert to an identifier
88 * @param[out] identifier by default, Postgres supports
89 * NAMEDATALEN=64 character identifiers
90 * @return end position of the identifier
91 */
92static char *
93sh_to_channel (struct GNUNET_ShortHashCode *sh,
94 char identifier[64])
95{
96 char *end;
97
98 end = GNUNET_STRINGS_data_to_string (sh,
99 sizeof (*sh),
100 identifier,
101 63);
102 GNUNET_assert (NULL != end);
103 *end = '\0';
104 return end;
105}
106
107
108/**
109 * Convert @a sh to a Postgres identifier.
110 *
111 * @param identifier to convert
112 * @param[out] sh set to short hash
113 * @return #GNUNET_OK on success
114 */
115static enum GNUNET_GenericReturnValue
116channel_to_sh (const char *identifier,
117 struct GNUNET_ShortHashCode *sh)
118{
119 return GNUNET_STRINGS_string_to_data (identifier,
120 strlen (identifier),
121 sh,
122 sizeof (*sh));
123}
124
125
126/**
127 * Convert @a es to a Postgres identifier.
128 *
129 * @param es spec to hash to an identifier
130 * @param[out] identifier by default, Postgres supports
131 * NAMEDATALEN=64 character identifiers
132 * @return end position of the identifier
133 */
134static char *
135es_to_channel (const struct GNUNET_DB_EventHeaderP *es,
136 char identifier[64])
137{
138 struct GNUNET_ShortHashCode sh;
139
140 es_to_sh (es,
141 &sh);
142 return sh_to_channel (&sh,
143 identifier);
144}
145
146
147/**
148 * Closure for #do_notify().
149 */
150struct NotifyContext
151{
152 /**
153 * Extra argument of the notification, or NULL.
154 */
155 void *extra;
156
157 /**
158 * Number of bytes in @e extra.
159 */
160 size_t extra_size;
161};
162
163
164/**
165 * Function called on every event handler that
166 * needs to be triggered.
167 *
168 * @param cls a `struct NotifyContext`
169 * @param sh channel name
170 * @param value a `struct GNUNET_DB_EventHandler`
171 * @return #GNUNET_OK continue to iterate
172 */
173static int
174do_notify (void *cls,
175 const struct GNUNET_ShortHashCode *sh,
176 void *value)
177{
178 struct NotifyContext *ctx = cls;
179 struct GNUNET_DB_EventHandler *eh = value;
180
181 eh->cb (eh->cb_cls,
182 ctx->extra,
183 ctx->extra_size);
184 return GNUNET_OK;
185}
186
187
188static void
189event_do_poll (struct GNUNET_PQ_Context *db)
190{
191 PGnotify *n;
192
193 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
194 "PG poll job active\n");
195 if (1 !=
196 PQconsumeInput (db->conn))
197 {
198 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
199 "Failed to read from Postgres: %s\n",
200 PQerrorMessage (db->conn));
201 if (CONNECTION_BAD != PQstatus (db->conn))
202 return;
203 GNUNET_PQ_reconnect (db);
204 return;
205 }
206 while (NULL != (n = PQnotifies (db->conn)))
207 {
208 struct GNUNET_ShortHashCode sh;
209 struct NotifyContext ctx = {
210 .extra = NULL
211 };
212
213 if ('X' != toupper ((int) n->relname[0]))
214 {
215 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
216 "Ignoring notification for unsupported channel identifier `%s'\n",
217 n->relname);
218 PQfreemem (n);
219 continue;
220 }
221 if (GNUNET_OK !=
222 channel_to_sh (&n->relname[1],
223 &sh))
224 {
225 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
226 "Ignoring notification for unsupported channel identifier `%s'\n",
227 n->relname);
228 PQfreemem (n);
229 continue;
230 }
231 if ( (NULL != n->extra) &&
232 (GNUNET_OK !=
233 GNUNET_STRINGS_string_to_data_alloc (n->extra,
234 strlen (n->extra),
235 &ctx.extra,
236 &ctx.extra_size)))
237 {
238 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
239 "Ignoring notification for unsupported extra data `%s' on channel `%s'\n",
240 n->extra,
241 n->relname);
242 PQfreemem (n);
243 continue;
244 }
245 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
246 "Received notification %s with extra data `%.*s'\n",
247 n->relname,
248 (int) ctx.extra_size,
249 (const char *) ctx.extra);
250 GNUNET_CONTAINER_multishortmap_get_multiple (db->channel_map,
251 &sh,
252 &do_notify,
253 &ctx);
254 GNUNET_free (ctx.extra);
255 PQfreemem (n);
256 }
257}
258
259
260/**
261 * The GNUnet scheduler notifies us that we need to
262 * trigger the DB event poller.
263 *
264 * @param cls a `struct GNUNET_PQ_Context *`
265 */
266static void
267do_scheduler_notify (void *cls)
268{
269 struct GNUNET_PQ_Context *db = cls;
270
271 db->event_task = NULL;
272 if (NULL == db->rfd)
273 GNUNET_PQ_reconnect (db);
274 event_do_poll (db);
275 if (NULL != db->event_task)
276 return;
277 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
278 "Resubscribing\n");
279 if (NULL == db->rfd)
280 {
281 db->event_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
282 &do_scheduler_notify,
283 db);
284 return;
285 }
286 db->event_task
287 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
288 db->rfd,
289 &do_scheduler_notify,
290 db);
291}
292
293
294/**
295 * Function called when the Postgres FD changes and we need
296 * to update the scheduler event loop task.
297 *
298 * @param cls a `struct GNUNET_PQ_Context *`
299 * @param fd the file descriptor, possibly -1
300 */
301static void
302scheduler_fd_cb (void *cls,
303 int fd)
304{
305 struct GNUNET_PQ_Context *db = cls;
306
307 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
308 "New poll FD is %d\n",
309 fd);
310 if (NULL != db->event_task)
311 {
312 GNUNET_SCHEDULER_cancel (db->event_task);
313 db->event_task = NULL;
314 }
315 GNUNET_free (db->rfd);
316 if (-1 == fd)
317 return;
318 if (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map))
319 return;
320 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
321 "Activating poll job on %d\n",
322 fd);
323 db->rfd = GNUNET_NETWORK_socket_box_native (fd);
324 db->event_task
325 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_ZERO,
326 db->rfd,
327 &do_scheduler_notify,
328 db);
329}
330
331
332/**
333 * Helper function to trigger an SQL @a cmd on @a db
334 *
335 * @param db database to send command to
336 * @param cmd prefix of the command to send
337 * @param eh details about the event
338 */
339static void
340manage_subscribe (struct GNUNET_PQ_Context *db,
341 const char *cmd,
342 struct GNUNET_DB_EventHandler *eh)
343{
344 char sql[16 + 64];
345 char *end;
346 PGresult *result;
347
348 if (NULL == db->conn)
349 return;
350 end = stpcpy (sql,
351 cmd);
352 end = sh_to_channel (&eh->sh,
353 end);
354 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
355 "Executing PQ command `%s'\n",
356 sql);
357 result = PQexec (db->conn,
358 sql);
359 if (PGRES_COMMAND_OK != PQresultStatus (result))
360 {
361 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
362 "pq",
363 "Failed to execute `%s': %s/%s/%s/%s/%s",
364 sql,
365 PQresultErrorField (result,
366 PG_DIAG_MESSAGE_PRIMARY),
367 PQresultErrorField (result,
368 PG_DIAG_MESSAGE_DETAIL),
369 PQresultErrorMessage (result),
370 PQresStatus (PQresultStatus (result)),
371 PQerrorMessage (db->conn));
372 }
373 PQclear (result);
374}
375
376
377/**
378 * Re-subscribe to notifications after disconnect.
379 *
380 * @param cls the DB context
381 * @param sh the short hash of the channel
382 * @param eh the event handler
383 * @return #GNUNET_OK to continue to iterate
384 */
385static int
386register_notify (void *cls,
387 const struct GNUNET_ShortHashCode *sh,
388 void *value)
389{
390 struct GNUNET_PQ_Context *db = cls;
391 struct GNUNET_DB_EventHandler *eh = value;
392
393 manage_subscribe (db,
394 "LISTEN X",
395 eh);
396 return GNUNET_OK;
397}
398
399
400void
401GNUNET_PQ_event_reconnect_ (struct GNUNET_PQ_Context *db,
402 int fd)
403{
404 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
405 "Change in PQ event FD to %d\n",
406 fd);
407 scheduler_fd_cb (db,
408 fd);
409 GNUNET_CONTAINER_multishortmap_iterate (db->channel_map,
410 &register_notify,
411 db);
412}
413
414
415/**
416 * Function run on timeout for an event. Triggers
417 * the notification, but does NOT clear the handler.
418 *
419 * @param cls a `struct GNUNET_DB_EventHandler *`
420 */
421static void
422event_timeout (void *cls)
423{
424 struct GNUNET_DB_EventHandler *eh = cls;
425
426 eh->timeout_task = NULL;
427 eh->cb (eh->cb_cls,
428 NULL,
429 0);
430}
431
432
433struct GNUNET_DB_EventHandler *
434GNUNET_PQ_event_listen (struct GNUNET_PQ_Context *db,
435 const struct GNUNET_DB_EventHeaderP *es,
436 struct GNUNET_TIME_Relative timeout,
437 GNUNET_DB_EventCallback cb,
438 void *cb_cls)
439{
440 struct GNUNET_DB_EventHandler *eh;
441 bool sub;
442
443 eh = GNUNET_new (struct GNUNET_DB_EventHandler);
444 eh->db = db;
445 es_to_sh (es,
446 &eh->sh);
447 eh->cb = cb;
448 eh->cb_cls = cb_cls;
449 sub = (NULL ==
450 GNUNET_CONTAINER_multishortmap_get (db->channel_map,
451 &eh->sh));
452 GNUNET_assert (GNUNET_OK ==
453 GNUNET_CONTAINER_multishortmap_put (db->channel_map,
454 &eh->sh,
455 eh,
456 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
457 if (NULL == db->event_task)
458 {
459 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
460 "Starting event scheduler\n");
461 scheduler_fd_cb (db,
462 PQsocket (db->conn));
463 }
464 if (sub)
465 manage_subscribe (db,
466 "LISTEN X",
467 eh);
468 eh->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout,
469 &event_timeout,
470 eh);
471 return eh;
472}
473
474
475void
476GNUNET_PQ_event_listen_cancel (struct GNUNET_DB_EventHandler *eh)
477{
478 struct GNUNET_PQ_Context *db = eh->db;
479
480 GNUNET_assert (GNUNET_OK ==
481 GNUNET_CONTAINER_multishortmap_remove (db->channel_map,
482 &eh->sh,
483 eh));
484 if (NULL ==
485 GNUNET_CONTAINER_multishortmap_get (db->channel_map,
486 &eh->sh))
487 manage_subscribe (db,
488 "UNLISTEN X",
489 eh);
490 if (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map))
491 {
492 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
493 "Stopping PQ event scheduler job\n");
494 GNUNET_free (db->rfd);
495 if (NULL != db->event_task)
496 {
497 GNUNET_SCHEDULER_cancel (db->event_task);
498 db->event_task = NULL;
499 }
500 }
501 if (NULL != eh->timeout_task)
502 {
503 GNUNET_SCHEDULER_cancel (eh->timeout_task);
504 eh->timeout_task = NULL;
505 }
506 GNUNET_free (eh);
507}
508
509
510void
511GNUNET_PQ_event_notify (struct GNUNET_PQ_Context *db,
512 const struct GNUNET_DB_EventHeaderP *es,
513 const void *extra,
514 size_t extra_size)
515{
516 char sql[16 + 64 + extra_size * 8 / 5 + 8];
517 char *end;
518 PGresult *result;
519
520 end = stpcpy (sql,
521 "NOTIFY X");
522 end = es_to_channel (es,
523 end);
524 end = stpcpy (end,
525 ", '");
526 end = GNUNET_STRINGS_data_to_string (extra,
527 extra_size,
528 end,
529 sizeof (sql) - (end - sql) - 1);
530 GNUNET_assert (NULL != end);
531 *end = '\0';
532 end = stpcpy (end,
533 "'");
534 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
535 "Executing command `%s'\n",
536 sql);
537 result = PQexec (db->conn,
538 sql);
539 if (PGRES_COMMAND_OK != PQresultStatus (result))
540 {
541 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
542 "pq",
543 "Failed to execute `%s': %s/%s/%s/%s/%s",
544 sql,
545 PQresultErrorField (result,
546 PG_DIAG_MESSAGE_PRIMARY),
547 PQresultErrorField (result,
548 PG_DIAG_MESSAGE_DETAIL),
549 PQresultErrorMessage (result),
550 PQresStatus (PQresultStatus (result)),
551 PQerrorMessage (db->conn));
552 }
553 PQclear (result);
554 event_do_poll (db);
555}
556
557
558/* end of pq_event.c */
diff --git a/src/pq/pq_exec.c b/src/pq/pq_exec.c
deleted file mode 100644
index dcde331b6..000000000
--- a/src/pq/pq_exec.c
+++ /dev/null
@@ -1,117 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2019 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_exec.c
22 * @brief functions to execute plain SQL statements (PostGres)
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "pq.h"
27
28
29/**
30 * Create a `struct GNUNET_PQ_ExecuteStatement` where errors are fatal.
31 *
32 * @param sql actual SQL statement
33 * @return initialized struct
34 */
35struct GNUNET_PQ_ExecuteStatement
36GNUNET_PQ_make_execute (const char *sql)
37{
38 struct GNUNET_PQ_ExecuteStatement es = {
39 .sql = sql,
40 .ignore_errors = GNUNET_NO
41 };
42
43 return es;
44}
45
46
47/**
48 * Create a `struct GNUNET_PQ_ExecuteStatement` where errors should
49 * be tolerated.
50 *
51 * @param sql actual SQL statement
52 * @return initialized struct
53 */
54struct GNUNET_PQ_ExecuteStatement
55GNUNET_PQ_make_try_execute (const char *sql)
56{
57 struct GNUNET_PQ_ExecuteStatement es = {
58 .sql = sql,
59 .ignore_errors = GNUNET_YES
60 };
61
62 return es;
63}
64
65
66/**
67 * Request execution of an array of statements @a es from Postgres.
68 *
69 * @param db database to execute the statements with
70 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
71 * statements.
72 * @return #GNUNET_OK on success (modulo statements where errors can be ignored)
73 * #GNUNET_SYSERR on error
74 */
75enum GNUNET_GenericReturnValue
76GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db,
77 const struct GNUNET_PQ_ExecuteStatement *es)
78{
79 for (unsigned int i = 0; NULL != es[i].sql; i++)
80 {
81 PGresult *result;
82
83 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
84 "Running statement `%s' on %p\n",
85 es[i].sql,
86 db);
87 result = PQexec (db->conn,
88 es[i].sql);
89 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
90 "Running statement `%s' on %p finished (%d)\n",
91 es[i].sql,
92 db,
93 PGRES_COMMAND_OK == PQresultStatus (result));
94 if ((GNUNET_NO == es[i].ignore_errors) &&
95 (PGRES_COMMAND_OK != PQresultStatus (result)))
96 {
97 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
98 "pq",
99 "Failed to execute `%s': %s/%s/%s/%s/%s",
100 es[i].sql,
101 PQresultErrorField (result,
102 PG_DIAG_MESSAGE_PRIMARY),
103 PQresultErrorField (result,
104 PG_DIAG_MESSAGE_DETAIL),
105 PQresultErrorMessage (result),
106 PQresStatus (PQresultStatus (result)),
107 PQerrorMessage (db->conn));
108 PQclear (result);
109 return GNUNET_SYSERR;
110 }
111 PQclear (result);
112 }
113 return GNUNET_OK;
114}
115
116
117/* end of pq/pq_exec.c */
diff --git a/src/pq/pq_prepare.c b/src/pq/pq_prepare.c
deleted file mode 100644
index 76fc5c374..000000000
--- a/src/pq/pq_prepare.c
+++ /dev/null
@@ -1,122 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2019 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_prepare.c
22 * @brief functions to connect to libpq (PostGres)
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "pq.h"
27
28
29/**
30 * Create a `struct GNUNET_PQ_PreparedStatement`.
31 *
32 * @param name name of the statement
33 * @param sql actual SQL statement
34 * @param num_args number of arguments in the statement
35 * @return initialized struct
36 */
37struct GNUNET_PQ_PreparedStatement
38GNUNET_PQ_make_prepare (const char *name,
39 const char *sql,
40 unsigned int num_args)
41{
42 struct GNUNET_PQ_PreparedStatement ps = {
43 .name = name,
44 .sql = sql,
45 .num_arguments = num_args
46 };
47
48 return ps;
49}
50
51
52/**
53 * Request creation of prepared statements @a ps from Postgres.
54 *
55 * @param db database to prepare the statements for
56 * @param ps #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
57 * statements.
58 * @return #GNUNET_OK on success,
59 * #GNUNET_SYSERR on error
60 */
61enum GNUNET_GenericReturnValue
62GNUNET_PQ_prepare_statements (struct GNUNET_PQ_Context *db,
63 const struct GNUNET_PQ_PreparedStatement *ps)
64{
65 if (db->ps != ps)
66 {
67 /* add 'ps' to list db->ps of prepared statements to run on reconnect! */
68 unsigned int olen = 0; /* length of existing 'db->ps' array */
69 unsigned int nlen = 0; /* length of 'ps' array */
70 struct GNUNET_PQ_PreparedStatement *rps; /* combined array */
71
72 if (NULL != db->ps)
73 while (NULL != db->ps[olen].name)
74 olen++;
75 while (NULL != ps[nlen].name)
76 nlen++;
77 rps = GNUNET_new_array (olen + nlen + 1,
78 struct GNUNET_PQ_PreparedStatement);
79 if (NULL != db->ps)
80 memcpy (rps,
81 db->ps,
82 olen * sizeof (struct GNUNET_PQ_PreparedStatement));
83 memcpy (&rps[olen],
84 ps,
85 nlen * sizeof (struct GNUNET_PQ_PreparedStatement));
86 GNUNET_free (db->ps);
87 db->ps = rps;
88 }
89
90 /* actually prepare statements */
91 for (unsigned int i = 0; NULL != ps[i].name; i++)
92 {
93 PGresult *ret;
94
95 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
96 "pq",
97 "Preparing SQL statement `%s' as `%s'\n",
98 ps[i].sql,
99 ps[i].name);
100 ret = PQprepare (db->conn,
101 ps[i].name,
102 ps[i].sql,
103 ps[i].num_arguments,
104 NULL);
105 if (PGRES_COMMAND_OK != PQresultStatus (ret))
106 {
107 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
108 "pq",
109 _ ("PQprepare (`%s' as `%s') failed with error: %s\n"),
110 ps[i].sql,
111 ps[i].name,
112 PQerrorMessage (db->conn));
113 PQclear (ret);
114 return GNUNET_SYSERR;
115 }
116 PQclear (ret);
117 }
118 return GNUNET_OK;
119}
120
121
122/* end of pq/pq_prepare.c */
diff --git a/src/pq/pq_query_helper.c b/src/pq/pq_query_helper.c
deleted file mode 100644
index cee84d203..000000000
--- a/src/pq/pq_query_helper.c
+++ /dev/null
@@ -1,520 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2016, 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 */
20/**
21 * @file pq/pq_query_helper.c
22 * @brief functions to initialize parameter arrays
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_pq_lib.h"
28
29
30/**
31 * Function called to convert input argument into SQL parameters.
32 *
33 * @param cls closure
34 * @param data pointer to input argument
35 * @param data_len number of bytes in @a data (if applicable)
36 * @param[out] param_values SQL data to set
37 * @param[out] param_lengths SQL length data to set
38 * @param[out] param_formats SQL format data to set
39 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
40 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
41 * @param scratch_length number of entries left in @a scratch
42 * @return -1 on error, number of offsets used in @a scratch otherwise
43 */
44static int
45qconv_null (void *cls,
46 const void *data,
47 size_t data_len,
48 void *param_values[],
49 int param_lengths[],
50 int param_formats[],
51 unsigned int param_length,
52 void *scratch[],
53 unsigned int scratch_length)
54{
55 (void) scratch;
56 (void) scratch_length;
57 (void) data;
58 (void) data_len;
59 GNUNET_break (NULL == cls);
60 if (1 != param_length)
61 return -1;
62 param_values[0] = NULL;
63 param_lengths[0] = 0;
64 param_formats[0] = 1;
65 return 0;
66}
67
68
69struct GNUNET_PQ_QueryParam
70GNUNET_PQ_query_param_null (void)
71{
72 struct GNUNET_PQ_QueryParam res = {
73 &qconv_null, NULL, NULL, 0, 1
74 };
75
76 return res;
77}
78
79
80/**
81 * Function called to convert input argument into SQL parameters.
82 *
83 * @param cls closure
84 * @param data pointer to input argument
85 * @param data_len number of bytes in @a data (if applicable)
86 * @param[out] param_values SQL data to set
87 * @param[out] param_lengths SQL length data to set
88 * @param[out] param_formats SQL format data to set
89 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
90 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
91 * @param scratch_length number of entries left in @a scratch
92 * @return -1 on error, number of offsets used in @a scratch otherwise
93 */
94static int
95qconv_fixed (void *cls,
96 const void *data,
97 size_t data_len,
98 void *param_values[],
99 int param_lengths[],
100 int param_formats[],
101 unsigned int param_length,
102 void *scratch[],
103 unsigned int scratch_length)
104{
105 (void) scratch;
106 (void) scratch_length;
107 GNUNET_break (NULL == cls);
108 if (1 != param_length)
109 return -1;
110 param_values[0] = (void *) data;
111 param_lengths[0] = data_len;
112 param_formats[0] = 1;
113 return 0;
114}
115
116
117struct GNUNET_PQ_QueryParam
118GNUNET_PQ_query_param_fixed_size (const void *ptr,
119 size_t ptr_size)
120{
121 struct GNUNET_PQ_QueryParam res = {
122 &qconv_fixed, NULL, ptr, ptr_size, 1
123 };
124
125 return res;
126}
127
128
129struct GNUNET_PQ_QueryParam
130GNUNET_PQ_query_param_string (const char *ptr)
131{
132 return GNUNET_PQ_query_param_fixed_size (ptr,
133 strlen (ptr));
134}
135
136
137/**
138 * Function called to convert input argument into SQL parameters.
139 *
140 * @param cls closure
141 * @param data pointer to input argument
142 * @param data_len number of bytes in @a data (if applicable)
143 * @param[out] param_values SQL data to set
144 * @param[out] param_lengths SQL length data to set
145 * @param[out] param_formats SQL format data to set
146 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
147 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
148 * @param scratch_length number of entries left in @a scratch
149 * @return -1 on error, number of offsets used in @a scratch otherwise
150 */
151static int
152qconv_uint16 (void *cls,
153 const void *data,
154 size_t data_len,
155 void *param_values[],
156 int param_lengths[],
157 int param_formats[],
158 unsigned int param_length,
159 void *scratch[],
160 unsigned int scratch_length)
161{
162 const uint16_t *u_hbo = data;
163 uint16_t *u_nbo;
164
165 (void) scratch;
166 (void) scratch_length;
167 GNUNET_break (NULL == cls);
168 if (1 != param_length)
169 return -1;
170 u_nbo = GNUNET_new (uint16_t);
171 scratch[0] = u_nbo;
172 *u_nbo = htons (*u_hbo);
173 param_values[0] = (void *) u_nbo;
174 param_lengths[0] = sizeof(uint16_t);
175 param_formats[0] = 1;
176 return 1;
177}
178
179
180struct GNUNET_PQ_QueryParam
181GNUNET_PQ_query_param_uint16 (const uint16_t *x)
182{
183 struct GNUNET_PQ_QueryParam res =
184 { &qconv_uint16, NULL, x, sizeof(*x), 1 };
185
186 return res;
187}
188
189
190/**
191 * Function called to convert input argument into SQL parameters.
192 *
193 * @param cls closure
194 * @param data pointer to input argument
195 * @param data_len number of bytes in @a data (if applicable)
196 * @param[out] param_values SQL data to set
197 * @param[out] param_lengths SQL length data to set
198 * @param[out] param_formats SQL format data to set
199 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
200 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
201 * @param scratch_length number of entries left in @a scratch
202 * @return -1 on error, number of offsets used in @a scratch otherwise
203 */
204static int
205qconv_uint32 (void *cls,
206 const void *data,
207 size_t data_len,
208 void *param_values[],
209 int param_lengths[],
210 int param_formats[],
211 unsigned int param_length,
212 void *scratch[],
213 unsigned int scratch_length)
214{
215 const uint32_t *u_hbo = data;
216 uint32_t *u_nbo;
217
218 (void) scratch;
219 (void) scratch_length;
220 GNUNET_break (NULL == cls);
221 if (1 != param_length)
222 return -1;
223 u_nbo = GNUNET_new (uint32_t);
224 scratch[0] = u_nbo;
225 *u_nbo = htonl (*u_hbo);
226 param_values[0] = (void *) u_nbo;
227 param_lengths[0] = sizeof(uint32_t);
228 param_formats[0] = 1;
229 return 1;
230}
231
232
233struct GNUNET_PQ_QueryParam
234GNUNET_PQ_query_param_uint32 (const uint32_t *x)
235{
236 struct GNUNET_PQ_QueryParam res =
237 { &qconv_uint32, NULL, x, sizeof(*x), 1 };
238
239 return res;
240}
241
242
243/**
244 * Function called to convert input argument into SQL parameters.
245 *
246 * @param cls closure
247 * @param data pointer to input argument
248 * @param data_len number of bytes in @a data (if applicable)
249 * @param[out] param_values SQL data to set
250 * @param[out] param_lengths SQL length data to set
251 * @param[out] param_formats SQL format data to set
252 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
253 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
254 * @param scratch_length number of entries left in @a scratch
255 * @return -1 on error, number of offsets used in @a scratch otherwise
256 */
257static int
258qconv_uint64 (void *cls,
259 const void *data,
260 size_t data_len,
261 void *param_values[],
262 int param_lengths[],
263 int param_formats[],
264 unsigned int param_length,
265 void *scratch[],
266 unsigned int scratch_length)
267{
268 const uint64_t *u_hbo = data;
269 uint64_t *u_nbo;
270
271 (void) scratch;
272 (void) scratch_length;
273 GNUNET_break (NULL == cls);
274 if (1 != param_length)
275 return -1;
276 u_nbo = GNUNET_new (uint64_t);
277 scratch[0] = u_nbo;
278 *u_nbo = GNUNET_htonll (*u_hbo);
279 param_values[0] = (void *) u_nbo;
280 param_lengths[0] = sizeof(uint64_t);
281 param_formats[0] = 1;
282 return 1;
283}
284
285
286struct GNUNET_PQ_QueryParam
287GNUNET_PQ_query_param_uint64 (const uint64_t *x)
288{
289 struct GNUNET_PQ_QueryParam res =
290 { &qconv_uint64, NULL, x, sizeof(*x), 1 };
291
292 return res;
293}
294
295
296/**
297 * Function called to convert input argument into SQL parameters.
298 *
299 * @param cls closure
300 * @param data pointer to input argument
301 * @param data_len number of bytes in @a data (if applicable)
302 * @param[out] param_values SQL data to set
303 * @param[out] param_lengths SQL length data to set
304 * @param[out] param_formats SQL format data to set
305 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
306 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
307 * @param scratch_length number of entries left in @a scratch
308 * @return -1 on error, number of offsets used in @a scratch otherwise
309 */
310static int
311qconv_rsa_public_key (void *cls,
312 const void *data,
313 size_t data_len,
314 void *param_values[],
315 int param_lengths[],
316 int param_formats[],
317 unsigned int param_length,
318 void *scratch[],
319 unsigned int scratch_length)
320{
321 const struct GNUNET_CRYPTO_RsaPublicKey *rsa = data;
322 void *buf;
323 size_t buf_size;
324
325 GNUNET_break (NULL == cls);
326 if (1 != param_length)
327 return -1;
328 buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rsa,
329 &buf);
330 scratch[0] = buf;
331 param_values[0] = (void *) buf;
332 param_lengths[0] = buf_size;
333 param_formats[0] = 1;
334 return 1;
335}
336
337
338struct GNUNET_PQ_QueryParam
339GNUNET_PQ_query_param_rsa_public_key (const struct
340 GNUNET_CRYPTO_RsaPublicKey *x)
341{
342 struct GNUNET_PQ_QueryParam res =
343 { &qconv_rsa_public_key, NULL, (x), 0, 1 };
344
345 return res;
346}
347
348
349/**
350 * Function called to convert input argument into SQL parameters.
351 *
352 * @param cls closure
353 * @param data pointer to input argument
354 * @param data_len number of bytes in @a data (if applicable)
355 * @param[out] param_values SQL data to set
356 * @param[out] param_lengths SQL length data to set
357 * @param[out] param_formats SQL format data to set
358 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
359 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
360 * @param scratch_length number of entries left in @a scratch
361 * @return -1 on error, number of offsets used in @a scratch otherwise
362 */
363static int
364qconv_rsa_signature (void *cls,
365 const void *data,
366 size_t data_len,
367 void *param_values[],
368 int param_lengths[],
369 int param_formats[],
370 unsigned int param_length,
371 void *scratch[],
372 unsigned int scratch_length)
373{
374 const struct GNUNET_CRYPTO_RsaSignature *sig = data;
375 void *buf;
376 size_t buf_size;
377
378 GNUNET_break (NULL == cls);
379 if (1 != param_length)
380 return -1;
381 buf_size = GNUNET_CRYPTO_rsa_signature_encode (sig,
382 &buf);
383 scratch[0] = buf;
384 param_values[0] = (void *) buf;
385 param_lengths[0] = buf_size;
386 param_formats[0] = 1;
387 return 1;
388}
389
390
391struct GNUNET_PQ_QueryParam
392GNUNET_PQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_RsaSignature *x)
393{
394 struct GNUNET_PQ_QueryParam res =
395 { &qconv_rsa_signature, NULL, (x), 0, 1 };
396
397 return res;
398}
399
400
401/**
402 * Function called to convert input argument into SQL parameters.
403 *
404 * @param cls closure
405 * @param data pointer to input argument
406 * @param data_len number of bytes in @a data (if applicable)
407 * @param[out] param_values SQL data to set
408 * @param[out] param_lengths SQL length data to set
409 * @param[out] param_formats SQL format data to set
410 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
411 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
412 * @param scratch_length number of entries left in @a scratch
413 * @return -1 on error, number of offsets used in @a scratch otherwise
414 */
415static int
416qconv_rel_time (void *cls,
417 const void *data,
418 size_t data_len,
419 void *param_values[],
420 int param_lengths[],
421 int param_formats[],
422 unsigned int param_length,
423 void *scratch[],
424 unsigned int scratch_length)
425{
426 const struct GNUNET_TIME_Relative *u = data;
427 struct GNUNET_TIME_Relative rel;
428 uint64_t *u_nbo;
429
430 GNUNET_break (NULL == cls);
431 if (1 != param_length)
432 return -1;
433 rel = *u;
434 if (rel.rel_value_us > INT64_MAX)
435 rel.rel_value_us = INT64_MAX;
436 u_nbo = GNUNET_new (uint64_t);
437 scratch[0] = u_nbo;
438 *u_nbo = GNUNET_htonll (rel.rel_value_us);
439 param_values[0] = (void *) u_nbo;
440 param_lengths[0] = sizeof(uint64_t);
441 param_formats[0] = 1;
442 return 1;
443}
444
445
446struct GNUNET_PQ_QueryParam
447GNUNET_PQ_query_param_relative_time (const struct GNUNET_TIME_Relative *x)
448{
449 struct GNUNET_PQ_QueryParam res =
450 { &qconv_rel_time, NULL, x, sizeof(*x), 1 };
451
452 return res;
453}
454
455
456/**
457 * Function called to convert input argument into SQL parameters.
458 *
459 * @param cls closure
460 * @param data pointer to input argument
461 * @param data_len number of bytes in @a data (if applicable)
462 * @param[out] param_values SQL data to set
463 * @param[out] param_lengths SQL length data to set
464 * @param[out] param_formats SQL format data to set
465 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
466 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
467 * @param scratch_length number of entries left in @a scratch
468 * @return -1 on error, number of offsets used in @a scratch otherwise
469 */
470static int
471qconv_abs_time (void *cls,
472 const void *data,
473 size_t data_len,
474 void *param_values[],
475 int param_lengths[],
476 int param_formats[],
477 unsigned int param_length,
478 void *scratch[],
479 unsigned int scratch_length)
480{
481 const struct GNUNET_TIME_Absolute *u = data;
482 struct GNUNET_TIME_Absolute abs;
483 uint64_t *u_nbo;
484
485 GNUNET_break (NULL == cls);
486 if (1 != param_length)
487 return -1;
488 abs = *u;
489 if (abs.abs_value_us > INT64_MAX)
490 abs.abs_value_us = INT64_MAX;
491 u_nbo = GNUNET_new (uint64_t);
492 scratch[0] = u_nbo;
493 *u_nbo = GNUNET_htonll (abs.abs_value_us);
494 param_values[0] = (void *) u_nbo;
495 param_lengths[0] = sizeof(uint64_t);
496 param_formats[0] = 1;
497 return 1;
498}
499
500
501struct GNUNET_PQ_QueryParam
502GNUNET_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x)
503{
504 struct GNUNET_PQ_QueryParam res = {
505 &qconv_abs_time, NULL, x, sizeof(*x), 1
506 };
507
508 return res;
509}
510
511
512struct GNUNET_PQ_QueryParam
513GNUNET_PQ_query_param_absolute_time_nbo (const struct
514 GNUNET_TIME_AbsoluteNBO *x)
515{
516 return GNUNET_PQ_query_param_auto_from_type (&x->abs_value_us__);
517}
518
519
520/* end of pq_query_helper.c */
diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c
deleted file mode 100644
index f264603f4..000000000
--- a/src/pq/pq_result_helper.c
+++ /dev/null
@@ -1,907 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2016, 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 */
20/**
21 * @file pq/pq_result_helper.c
22 * @brief functions to extract result values
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26#include "gnunet_util_lib.h"
27#include "gnunet_pq_lib.h"
28
29
30struct GNUNET_PQ_ResultSpec
31GNUNET_PQ_result_spec_allow_null (struct GNUNET_PQ_ResultSpec rs,
32 bool *is_null)
33{
34 struct GNUNET_PQ_ResultSpec rsr;
35
36 rsr = rs;
37 rsr.is_nullable = true;
38 rsr.is_null = is_null;
39 return rsr;
40}
41
42
43/**
44 * Function called to clean up memory allocated
45 * by a #GNUNET_PQ_ResultConverter.
46 *
47 * @param cls closure
48 * @param rd result data to clean up
49 */
50static void
51clean_varsize_blob (void *cls,
52 void *rd)
53{
54 void **dst = rd;
55
56 (void) cls;
57 if (NULL != *dst)
58 {
59 GNUNET_free (*dst);
60 *dst = NULL;
61 }
62}
63
64
65/**
66 * Extract data from a Postgres database @a result at row @a row.
67 *
68 * @param cls closure
69 * @param result where to extract data from
70 * @param int row to extract data from
71 * @param fname name (or prefix) of the fields to extract from
72 * @param[in,out] dst_size where to store size of result, may be NULL
73 * @param[out] dst where to store the result
74 * @return
75 * #GNUNET_YES if all results could be extracted
76 * #GNUNET_SYSERR if a result was invalid (non-existing field)
77 */
78static enum GNUNET_GenericReturnValue
79extract_varsize_blob (void *cls,
80 PGresult *result,
81 int row,
82 const char *fname,
83 size_t *dst_size,
84 void *dst)
85{
86 size_t len;
87 const char *res;
88 void *idst;
89 int fnum;
90
91 (void) cls;
92 *dst_size = 0;
93 *((void **) dst) = NULL;
94
95 fnum = PQfnumber (result,
96 fname);
97 if (fnum < 0)
98 {
99 GNUNET_break (0);
100 return GNUNET_SYSERR;
101 }
102 if (PQgetisnull (result,
103 row,
104 fnum))
105 return GNUNET_NO;
106 /* if a field is null, continue but
107 * remember that we now return a different result */
108 len = PQgetlength (result,
109 row,
110 fnum);
111 res = PQgetvalue (result,
112 row,
113 fnum);
114 GNUNET_assert (NULL != res);
115 *dst_size = len;
116 idst = GNUNET_malloc (len);
117 *((void **) dst) = idst;
118 GNUNET_memcpy (idst,
119 res,
120 len);
121 return GNUNET_OK;
122}
123
124
125struct GNUNET_PQ_ResultSpec
126GNUNET_PQ_result_spec_variable_size (const char *name,
127 void **dst,
128 size_t *sptr)
129{
130 struct GNUNET_PQ_ResultSpec res = {
131 .conv = &extract_varsize_blob,
132 .cleaner = &clean_varsize_blob,
133 .dst = (void *) (dst),
134 .fname = name,
135 .result_size = sptr
136 };
137
138 return res;
139}
140
141
142/**
143 * Extract data from a Postgres database @a result at row @a row.
144 *
145 * @param cls closure
146 * @param result where to extract data from
147 * @param int row to extract data from
148 * @param fname name (or prefix) of the fields to extract from
149 * @param[in] dst_size desired size, never NULL
150 * @param[out] dst where to store the result
151 * @return
152 * #GNUNET_YES if all results could be extracted
153 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
154 */
155static enum GNUNET_GenericReturnValue
156extract_fixed_blob (void *cls,
157 PGresult *result,
158 int row,
159 const char *fname,
160 size_t *dst_size,
161 void *dst)
162{
163 size_t len;
164 const char *res;
165 int fnum;
166
167 (void) cls;
168 fnum = PQfnumber (result,
169 fname);
170 if (fnum < 0)
171 {
172 GNUNET_break (0);
173 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
174 "Result does not have field %s\n",
175 fname);
176 return GNUNET_SYSERR;
177 }
178 if (PQgetisnull (result,
179 row,
180 fnum))
181 return GNUNET_NO;
182 /* if a field is null, continue but
183 * remember that we now return a different result */
184 len = PQgetlength (result,
185 row,
186 fnum);
187 if (*dst_size != len)
188 {
189 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
190 "Expected %u bytes for field `%s', got %u\n",
191 (unsigned int) *dst_size,
192 fname,
193 (unsigned int) len);
194 GNUNET_break (0);
195 return GNUNET_SYSERR;
196 }
197 res = PQgetvalue (result,
198 row,
199 fnum);
200 GNUNET_assert (NULL != res);
201 GNUNET_memcpy (dst,
202 res,
203 len);
204 return GNUNET_OK;
205}
206
207
208struct GNUNET_PQ_ResultSpec
209GNUNET_PQ_result_spec_fixed_size (const char *name,
210 void *dst,
211 size_t dst_size)
212{
213 struct GNUNET_PQ_ResultSpec res = {
214 .conv = &extract_fixed_blob,
215 .dst = (dst),
216 .dst_size = dst_size,
217 .fname = name
218 };
219
220 return res;
221}
222
223
224/**
225 * Extract data from a Postgres database @a result at row @a row.
226 *
227 * @param cls closure
228 * @param result where to extract data from
229 * @param int row to extract data from
230 * @param fname name (or prefix) of the fields to extract from
231 * @param[in,out] dst_size where to store size of result, may be NULL
232 * @param[out] dst where to store the result
233 * @return
234 * #GNUNET_YES if all results could be extracted
235 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
236 */
237static enum GNUNET_GenericReturnValue
238extract_rsa_public_key (void *cls,
239 PGresult *result,
240 int row,
241 const char *fname,
242 size_t *dst_size,
243 void *dst)
244{
245 struct GNUNET_CRYPTO_RsaPublicKey **pk = dst;
246 size_t len;
247 const char *res;
248 int fnum;
249
250 (void) cls;
251 *pk = NULL;
252 fnum = PQfnumber (result,
253 fname);
254 if (fnum < 0)
255 {
256 GNUNET_break (0);
257 return GNUNET_SYSERR;
258 }
259 if (PQgetisnull (result,
260 row,
261 fnum))
262 return GNUNET_NO;
263
264 /* if a field is null, continue but
265 * remember that we now return a different result */
266 len = PQgetlength (result,
267 row,
268 fnum);
269 res = PQgetvalue (result,
270 row,
271 fnum);
272 *pk = GNUNET_CRYPTO_rsa_public_key_decode (res,
273 len);
274 if (NULL == *pk)
275 {
276 GNUNET_break (0);
277 return GNUNET_SYSERR;
278 }
279 return GNUNET_OK;
280}
281
282
283/**
284 * Function called to clean up memory allocated
285 * by a #GNUNET_PQ_ResultConverter.
286 *
287 * @param cls closure
288 * @param rd result data to clean up
289 */
290static void
291clean_rsa_public_key (void *cls,
292 void *rd)
293{
294 struct GNUNET_CRYPTO_RsaPublicKey **pk = rd;
295
296 (void) cls;
297 if (NULL != *pk)
298 {
299 GNUNET_CRYPTO_rsa_public_key_free (*pk);
300 *pk = NULL;
301 }
302}
303
304
305struct GNUNET_PQ_ResultSpec
306GNUNET_PQ_result_spec_rsa_public_key (const char *name,
307 struct GNUNET_CRYPTO_RsaPublicKey **rsa)
308{
309 struct GNUNET_PQ_ResultSpec res = {
310 .conv = &extract_rsa_public_key,
311 .cleaner = &clean_rsa_public_key,
312 .dst = (void *) rsa,
313 .fname = name
314 };
315
316 return res;
317}
318
319
320/**
321 * Extract data from a Postgres database @a result at row @a row.
322 *
323 * @param cls closure
324 * @param result where to extract data from
325 * @param int row to extract data from
326 * @param fname name (or prefix) of the fields to extract from
327 * @param[in,out] dst_size where to store size of result, may be NULL
328 * @param[out] dst where to store the result
329 * @return
330 * #GNUNET_YES if all results could be extracted
331 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
332 */
333static enum GNUNET_GenericReturnValue
334extract_rsa_signature (void *cls,
335 PGresult *result,
336 int row,
337 const char *fname,
338 size_t *dst_size,
339 void *dst)
340{
341 struct GNUNET_CRYPTO_RsaSignature **sig = dst;
342 size_t len;
343 const void *res;
344 int fnum;
345
346 (void) cls;
347 *sig = NULL;
348 fnum = PQfnumber (result,
349 fname);
350 if (fnum < 0)
351 {
352 GNUNET_break (0);
353 return GNUNET_SYSERR;
354 }
355 if (PQgetisnull (result,
356 row,
357 fnum))
358 return GNUNET_NO;
359 /* if a field is null, continue but
360 * remember that we now return a different result */
361 len = PQgetlength (result,
362 row,
363 fnum);
364 res = PQgetvalue (result,
365 row,
366 fnum);
367 *sig = GNUNET_CRYPTO_rsa_signature_decode (res,
368 len);
369 if (NULL == *sig)
370 {
371 GNUNET_break (0);
372 return GNUNET_SYSERR;
373 }
374 return GNUNET_OK;
375}
376
377
378/**
379 * Function called to clean up memory allocated
380 * by a #GNUNET_PQ_ResultConverter.
381 *
382 * @param cls closure
383 * @param rd result data to clean up
384 */
385static void
386clean_rsa_signature (void *cls,
387 void *rd)
388{
389 struct GNUNET_CRYPTO_RsaSignature **sig = rd;
390
391 (void) cls;
392 if (NULL != *sig)
393 {
394 GNUNET_CRYPTO_rsa_signature_free (*sig);
395 *sig = NULL;
396 }
397}
398
399
400struct GNUNET_PQ_ResultSpec
401GNUNET_PQ_result_spec_rsa_signature (const char *name,
402 struct GNUNET_CRYPTO_RsaSignature **sig)
403{
404 struct GNUNET_PQ_ResultSpec res = {
405 .conv = &extract_rsa_signature,
406 .cleaner = &clean_rsa_signature,
407 .dst = (void *) sig,
408 .fname = name
409 };
410
411 return res;
412}
413
414
415/**
416 * Extract data from a Postgres database @a result at row @a row.
417 *
418 * @param cls closure
419 * @param result where to extract data from
420 * @param int row to extract data from
421 * @param fname name (or prefix) of the fields to extract from
422 * @param[in,out] dst_size where to store size of result, may be NULL
423 * @param[out] dst where to store the result
424 * @return
425 * #GNUNET_YES if all results could be extracted
426 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
427 */
428static enum GNUNET_GenericReturnValue
429extract_string (void *cls,
430 PGresult *result,
431 int row,
432 const char *fname,
433 size_t *dst_size,
434 void *dst)
435{
436 char **str = dst;
437 size_t len;
438 const char *res;
439 int fnum;
440
441 (void) cls;
442 *str = NULL;
443 fnum = PQfnumber (result,
444 fname);
445 if (fnum < 0)
446 {
447 GNUNET_break (0);
448 return GNUNET_SYSERR;
449 }
450 if (PQgetisnull (result,
451 row,
452 fnum))
453 return GNUNET_NO;
454 /* if a field is null, continue but
455 * remember that we now return a different result */
456 len = PQgetlength (result,
457 row,
458 fnum);
459 res = PQgetvalue (result,
460 row,
461 fnum);
462 *str = GNUNET_strndup (res,
463 len);
464 if (NULL == *str)
465 {
466 GNUNET_break (0);
467 return GNUNET_SYSERR;
468 }
469 return GNUNET_OK;
470}
471
472
473/**
474 * Function called to clean up memory allocated
475 * by a #GNUNET_PQ_ResultConverter.
476 *
477 * @param cls closure
478 * @param rd result data to clean up
479 */
480static void
481clean_string (void *cls,
482 void *rd)
483{
484 char **str = rd;
485
486 (void) cls;
487 if (NULL != *str)
488 {
489 GNUNET_free (*str);
490 *str = NULL;
491 }
492}
493
494
495struct GNUNET_PQ_ResultSpec
496GNUNET_PQ_result_spec_string (const char *name,
497 char **dst)
498{
499 struct GNUNET_PQ_ResultSpec res =
500 { &extract_string,
501 &clean_string,
502 NULL,
503 (void *) dst, 0, (name), NULL };
504
505 return res;
506}
507
508
509/**
510 * Extract data from a Postgres database @a result at row @a row.
511 *
512 * @param cls closure
513 * @param result where to extract data from
514 * @param int row to extract data from
515 * @param fname name (or prefix) of the fields to extract from
516 * @param[in,out] dst_size where to store size of result, may be NULL
517 * @param[out] dst where to store the result
518 * @return
519 * #GNUNET_YES if all results could be extracted
520 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
521 */
522static enum GNUNET_GenericReturnValue
523extract_rel_time (void *cls,
524 PGresult *result,
525 int row,
526 const char *fname,
527 size_t *dst_size,
528 void *dst)
529{
530 struct GNUNET_TIME_Relative *udst = dst;
531 const int64_t *res;
532 int fnum;
533
534 (void) cls;
535 fnum = PQfnumber (result,
536 fname);
537 if (fnum < 0)
538 {
539 GNUNET_break (0);
540 return GNUNET_SYSERR;
541 }
542 if (PQgetisnull (result,
543 row,
544 fnum))
545 return GNUNET_NO;
546 GNUNET_assert (NULL != dst);
547 if (sizeof(struct GNUNET_TIME_Relative) != *dst_size)
548 {
549 GNUNET_break (0);
550 return GNUNET_SYSERR;
551 }
552 if (sizeof(int64_t) !=
553 PQgetlength (result,
554 row,
555 fnum))
556 {
557 GNUNET_break (0);
558 return GNUNET_SYSERR;
559 }
560 res = (int64_t *) PQgetvalue (result,
561 row,
562 fnum);
563 if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res))
564 *udst = GNUNET_TIME_UNIT_FOREVER_REL;
565 else
566 udst->rel_value_us = GNUNET_ntohll ((uint64_t) *res);
567 return GNUNET_OK;
568}
569
570
571struct GNUNET_PQ_ResultSpec
572GNUNET_PQ_result_spec_relative_time (const char *name,
573 struct GNUNET_TIME_Relative *rt)
574{
575 struct GNUNET_PQ_ResultSpec res = {
576 &extract_rel_time,
577 NULL,
578 NULL,
579 (void *) rt,
580 sizeof(*rt),
581 name,
582 NULL
583 };
584
585 return res;
586}
587
588
589/**
590 * Extract data from a Postgres database @a result at row @a row.
591 *
592 * @param cls closure
593 * @param result where to extract data from
594 * @param int row to extract data from
595 * @param fname name (or prefix) of the fields to extract from
596 * @param[in,out] dst_size where to store size of result, may be NULL
597 * @param[out] dst where to store the result
598 * @return
599 * #GNUNET_YES if all results could be extracted
600 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
601 */
602static enum GNUNET_GenericReturnValue
603extract_abs_time (void *cls,
604 PGresult *result,
605 int row,
606 const char *fname,
607 size_t *dst_size,
608 void *dst)
609{
610 struct GNUNET_TIME_Absolute *udst = dst;
611 const int64_t *res;
612 int fnum;
613
614 (void) cls;
615 fnum = PQfnumber (result,
616 fname);
617 if (fnum < 0)
618 {
619 GNUNET_break (0);
620 return GNUNET_SYSERR;
621 }
622 if (PQgetisnull (result,
623 row,
624 fnum))
625 return GNUNET_NO;
626 GNUNET_assert (NULL != dst);
627 if (sizeof(struct GNUNET_TIME_Absolute) != *dst_size)
628 {
629 GNUNET_break (0);
630 return GNUNET_SYSERR;
631 }
632 if (sizeof(int64_t) !=
633 PQgetlength (result,
634 row,
635 fnum))
636 {
637 GNUNET_break (0);
638 return GNUNET_SYSERR;
639 }
640 res = (int64_t *) PQgetvalue (result,
641 row,
642 fnum);
643 if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res))
644 *udst = GNUNET_TIME_UNIT_FOREVER_ABS;
645 else
646 udst->abs_value_us = GNUNET_ntohll ((uint64_t) *res);
647 return GNUNET_OK;
648}
649
650
651struct GNUNET_PQ_ResultSpec
652GNUNET_PQ_result_spec_absolute_time (const char *name,
653 struct GNUNET_TIME_Absolute *at)
654{
655 struct GNUNET_PQ_ResultSpec res =
656 { &extract_abs_time,
657 NULL,
658 NULL,
659 (void *) at, sizeof(*at), (name), NULL };
660
661 return res;
662}
663
664
665struct GNUNET_PQ_ResultSpec
666GNUNET_PQ_result_spec_absolute_time_nbo (const char *name,
667 struct GNUNET_TIME_AbsoluteNBO *at)
668{
669 struct GNUNET_PQ_ResultSpec res =
670 GNUNET_PQ_result_spec_auto_from_type (name, &at->abs_value_us__);
671
672 return res;
673}
674
675
676/**
677 * Extract data from a Postgres database @a result at row @a row.
678 *
679 * @param cls closure
680 * @param result where to extract data from
681 * @param int row to extract data from
682 * @param fname name (or prefix) of the fields to extract from
683 * @param[in,out] dst_size where to store size of result, may be NULL
684 * @param[out] dst where to store the result
685 * @return
686 * #GNUNET_YES if all results could be extracted
687 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
688 */
689static enum GNUNET_GenericReturnValue
690extract_uint16 (void *cls,
691 PGresult *result,
692 int row,
693 const char *fname,
694 size_t *dst_size,
695 void *dst)
696{
697 uint16_t *udst = dst;
698 const uint16_t *res;
699 int fnum;
700
701 (void) cls;
702 fnum = PQfnumber (result,
703 fname);
704 if (fnum < 0)
705 {
706 GNUNET_break (0);
707 return GNUNET_SYSERR;
708 }
709 if (PQgetisnull (result,
710 row,
711 fnum))
712 return GNUNET_NO;
713 GNUNET_assert (NULL != dst);
714 if (sizeof(uint16_t) != *dst_size)
715 {
716 GNUNET_break (0);
717 return GNUNET_SYSERR;
718 }
719 if (sizeof(uint16_t) !=
720 PQgetlength (result,
721 row,
722 fnum))
723 {
724 GNUNET_break (0);
725 return GNUNET_SYSERR;
726 }
727 res = (uint16_t *) PQgetvalue (result,
728 row,
729 fnum);
730 *udst = ntohs (*res);
731 return GNUNET_OK;
732}
733
734
735struct GNUNET_PQ_ResultSpec
736GNUNET_PQ_result_spec_uint16 (const char *name,
737 uint16_t *u16)
738{
739 struct GNUNET_PQ_ResultSpec res =
740 { &extract_uint16,
741 NULL,
742 NULL,
743 (void *) u16, sizeof(*u16), (name), NULL };
744
745 return res;
746}
747
748
749/**
750 * Extract data from a Postgres database @a result at row @a row.
751 *
752 * @param cls closure
753 * @param result where to extract data from
754 * @param int row to extract data from
755 * @param fname name (or prefix) of the fields to extract from
756 * @param[in,out] dst_size where to store size of result, may be NULL
757 * @param[out] dst where to store the result
758 * @return
759 * #GNUNET_YES if all results could be extracted
760 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
761 */
762static enum GNUNET_GenericReturnValue
763extract_uint32 (void *cls,
764 PGresult *result,
765 int row,
766 const char *fname,
767 size_t *dst_size,
768 void *dst)
769{
770 uint32_t *udst = dst;
771 const uint32_t *res;
772 int fnum;
773
774 (void) cls;
775 fnum = PQfnumber (result,
776 fname);
777 if (fnum < 0)
778 {
779 GNUNET_break (0);
780 return GNUNET_SYSERR;
781 }
782 if (PQgetisnull (result,
783 row,
784 fnum))
785 return GNUNET_NO;
786 GNUNET_assert (NULL != dst);
787 if (sizeof(uint32_t) != *dst_size)
788 {
789 GNUNET_break (0);
790 return GNUNET_SYSERR;
791 }
792 if (sizeof(uint32_t) !=
793 PQgetlength (result,
794 row,
795 fnum))
796 {
797 GNUNET_break (0);
798 return GNUNET_SYSERR;
799 }
800 res = (uint32_t *) PQgetvalue (result,
801 row,
802 fnum);
803 *udst = ntohl (*res);
804 return GNUNET_OK;
805}
806
807
808struct GNUNET_PQ_ResultSpec
809GNUNET_PQ_result_spec_uint32 (const char *name,
810 uint32_t *u32)
811{
812 struct GNUNET_PQ_ResultSpec res = {
813 &extract_uint32,
814 NULL,
815 NULL,
816 (void *) u32,
817 sizeof(*u32),
818 (name),
819 NULL
820 };
821
822 return res;
823}
824
825
826/**
827 * Extract data from a Postgres database @a result at row @a row.
828 *
829 * @param cls closure
830 * @param result where to extract data from
831 * @param int row to extract data from
832 * @param fname name (or prefix) of the fields to extract from
833 * @param[in,out] dst_size where to store size of result, may be NULL
834 * @param[out] dst where to store the result
835 * @return
836 * #GNUNET_YES if all results could be extracted
837 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
838 */
839static enum GNUNET_GenericReturnValue
840extract_uint64 (void *cls,
841 PGresult *result,
842 int row,
843 const char *fname,
844 size_t *dst_size,
845 void *dst)
846{
847 uint64_t *udst = dst;
848 const uint64_t *res;
849 int fnum;
850
851 (void) cls;
852 fnum = PQfnumber (result,
853 fname);
854 if (fnum < 0)
855 {
856 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
857 "Field %s missing in result\n",
858 fname);
859 GNUNET_break (0);
860 return GNUNET_SYSERR;
861 }
862 if (PQgetisnull (result,
863 row,
864 fnum))
865 return GNUNET_NO;
866
867 GNUNET_assert (NULL != dst);
868 if (sizeof(uint64_t) != *dst_size)
869 {
870 GNUNET_break (0);
871 return GNUNET_SYSERR;
872 }
873 if (sizeof(uint64_t) !=
874 PQgetlength (result,
875 row,
876 fnum))
877 {
878 GNUNET_break (0);
879 return GNUNET_SYSERR;
880 }
881 res = (uint64_t *) PQgetvalue (result,
882 row,
883 fnum);
884 *udst = GNUNET_ntohll (*res);
885 return GNUNET_OK;
886}
887
888
889struct GNUNET_PQ_ResultSpec
890GNUNET_PQ_result_spec_uint64 (const char *name,
891 uint64_t *u64)
892{
893 struct GNUNET_PQ_ResultSpec res = {
894 &extract_uint64,
895 NULL,
896 NULL,
897 (void *) u64,
898 sizeof(*u64),
899 (name),
900 NULL
901 };
902
903 return res;
904}
905
906
907/* end of pq_result_helper.c */
diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c
deleted file mode 100644
index 90b5c6489..000000000
--- a/src/pq/test_pq.c
+++ /dev/null
@@ -1,421 +0,0 @@
1/*
2 This file is part of GNUnet
3 (C) 2015, 2016, 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 */
20/**
21 * @file pq/test_pq.c
22 * @brief Tests for Postgres convenience API
23 * @author Christian Grothoff <christian@grothoff.org>
24 */
25#include "platform.h"
26#include "pq.h"
27
28/**
29 * Database handle.
30 */
31static struct GNUNET_PQ_Context *db;
32
33/**
34 * Global return value, 0 on success.
35 */
36static int ret;
37
38/**
39 * An event handler.
40 */
41static struct GNUNET_DB_EventHandler *eh;
42
43/**
44 * Timeout task.
45 */
46static struct GNUNET_SCHEDULER_Task *tt;
47
48
49/**
50 * Setup prepared statements.
51 *
52 * @param db database handle to initialize
53 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
54 */
55static int
56postgres_prepare (struct GNUNET_PQ_Context *db)
57{
58 struct GNUNET_PQ_PreparedStatement ps[] = {
59 GNUNET_PQ_make_prepare ("test_insert",
60 "INSERT INTO test_pq ("
61 " pub"
62 ",sig"
63 ",abs_time"
64 ",forever"
65 ",hash"
66 ",vsize"
67 ",u16"
68 ",u32"
69 ",u64"
70 ",unn"
71 ") VALUES "
72 "($1, $2, $3, $4, $5, $6,"
73 "$7, $8, $9, $10);",
74 10),
75 GNUNET_PQ_make_prepare ("test_select",
76 "SELECT"
77 " pub"
78 ",sig"
79 ",abs_time"
80 ",forever"
81 ",hash"
82 ",vsize"
83 ",u16"
84 ",u32"
85 ",u64"
86 ",unn"
87 " FROM test_pq"
88 " ORDER BY abs_time DESC "
89 " LIMIT 1;",
90 0),
91 GNUNET_PQ_PREPARED_STATEMENT_END
92 };
93
94 return GNUNET_PQ_prepare_statements (db,
95 ps);
96}
97
98
99/**
100 * Run actual test queries.
101 *
102 * @param db database handle
103 * @return 0 on success
104 */
105static int
106run_queries (struct GNUNET_PQ_Context *db)
107{
108 struct GNUNET_CRYPTO_RsaPublicKey *pub;
109 struct GNUNET_CRYPTO_RsaPublicKey *pub2 = NULL;
110 struct GNUNET_CRYPTO_RsaSignature *sig;
111 struct GNUNET_CRYPTO_RsaSignature *sig2 = NULL;
112 struct GNUNET_TIME_Absolute abs_time = GNUNET_TIME_absolute_get ();
113 struct GNUNET_TIME_Absolute abs_time2;
114 struct GNUNET_TIME_Absolute forever = GNUNET_TIME_UNIT_FOREVER_ABS;
115 struct GNUNET_TIME_Absolute forever2;
116 struct GNUNET_HashCode hc;
117 struct GNUNET_HashCode hc2;
118 PGresult *result;
119 int ret;
120 struct GNUNET_CRYPTO_RsaPrivateKey *priv;
121 const char msg[] = "hello";
122 void *msg2;
123 struct GNUNET_HashCode hmsg;
124 size_t msg2_len;
125 uint16_t u16;
126 uint16_t u162;
127 uint32_t u32;
128 uint32_t u322;
129 uint64_t u64;
130 uint64_t u642;
131 uint64_t uzzz = 42;
132
133 priv = GNUNET_CRYPTO_rsa_private_key_create (1024);
134 pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
135 memset (&hmsg, 42, sizeof(hmsg));
136 sig = GNUNET_CRYPTO_rsa_sign_fdh (priv,
137 &hmsg);
138 u16 = 16;
139 u32 = 32;
140 u64 = 64;
141 /* FIXME: test GNUNET_PQ_result_spec_variable_size */
142 {
143 struct GNUNET_PQ_QueryParam params_insert[] = {
144 GNUNET_PQ_query_param_rsa_public_key (pub),
145 GNUNET_PQ_query_param_rsa_signature (sig),
146 GNUNET_PQ_query_param_absolute_time (&abs_time),
147 GNUNET_PQ_query_param_absolute_time (&forever),
148 GNUNET_PQ_query_param_auto_from_type (&hc),
149 GNUNET_PQ_query_param_fixed_size (msg, strlen (msg)),
150 GNUNET_PQ_query_param_uint16 (&u16),
151 GNUNET_PQ_query_param_uint32 (&u32),
152 GNUNET_PQ_query_param_uint64 (&u64),
153 GNUNET_PQ_query_param_null (),
154 GNUNET_PQ_query_param_end
155 };
156 struct GNUNET_PQ_QueryParam params_select[] = {
157 GNUNET_PQ_query_param_end
158 };
159 bool got_null = false;
160 struct GNUNET_PQ_ResultSpec results_select[] = {
161 GNUNET_PQ_result_spec_rsa_public_key ("pub", &pub2),
162 GNUNET_PQ_result_spec_rsa_signature ("sig", &sig2),
163 GNUNET_PQ_result_spec_absolute_time ("abs_time", &abs_time2),
164 GNUNET_PQ_result_spec_absolute_time ("forever", &forever2),
165 GNUNET_PQ_result_spec_auto_from_type ("hash", &hc2),
166 GNUNET_PQ_result_spec_variable_size ("vsize", &msg2, &msg2_len),
167 GNUNET_PQ_result_spec_uint16 ("u16", &u162),
168 GNUNET_PQ_result_spec_uint32 ("u32", &u322),
169 GNUNET_PQ_result_spec_uint64 ("u64", &u642),
170 GNUNET_PQ_result_spec_allow_null (
171 GNUNET_PQ_result_spec_uint64 ("unn", &uzzz),
172 &got_null),
173 GNUNET_PQ_result_spec_end
174 };
175
176 result = GNUNET_PQ_exec_prepared (db,
177 "test_insert",
178 params_insert);
179 if (PGRES_COMMAND_OK != PQresultStatus (result))
180 {
181 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
182 "Database failure: %s\n",
183 PQresultErrorMessage (result));
184 PQclear (result);
185 GNUNET_CRYPTO_rsa_signature_free (sig);
186 GNUNET_CRYPTO_rsa_private_key_free (priv);
187 GNUNET_CRYPTO_rsa_public_key_free (pub);
188 return 1;
189 }
190
191 PQclear (result);
192 result = GNUNET_PQ_exec_prepared (db,
193 "test_select",
194 params_select);
195 if (1 !=
196 PQntuples (result))
197 {
198 GNUNET_break (0);
199 PQclear (result);
200 GNUNET_CRYPTO_rsa_signature_free (sig);
201 GNUNET_CRYPTO_rsa_private_key_free (priv);
202 GNUNET_CRYPTO_rsa_public_key_free (pub);
203 return 1;
204 }
205 ret = GNUNET_PQ_extract_result (result,
206 results_select,
207 0);
208 GNUNET_break (GNUNET_YES == ret);
209 GNUNET_break (abs_time.abs_value_us == abs_time2.abs_value_us);
210 GNUNET_break (forever.abs_value_us == forever2.abs_value_us);
211 GNUNET_break (0 ==
212 GNUNET_memcmp (&hc,
213 &hc2));
214 GNUNET_break (0 ==
215 GNUNET_CRYPTO_rsa_signature_cmp (sig,
216 sig2));
217 GNUNET_break (0 ==
218 GNUNET_CRYPTO_rsa_public_key_cmp (pub,
219 pub2));
220 GNUNET_break (strlen (msg) == msg2_len);
221 GNUNET_break (0 ==
222 strncmp (msg,
223 msg2,
224 msg2_len));
225 GNUNET_break (16 == u162);
226 GNUNET_break (32 == u322);
227 GNUNET_break (64 == u642);
228 GNUNET_break (42 == uzzz);
229 GNUNET_break (got_null);
230 GNUNET_PQ_cleanup_result (results_select);
231 PQclear (result);
232 }
233 GNUNET_CRYPTO_rsa_signature_free (sig);
234 GNUNET_CRYPTO_rsa_private_key_free (priv);
235 GNUNET_CRYPTO_rsa_public_key_free (pub);
236 if (GNUNET_OK != ret)
237 return 1;
238
239 return 0;
240}
241
242
243/**
244 * Task called on shutdown.
245 *
246 * @param cls NULL
247 */
248static void
249event_end (void *cls)
250{
251 GNUNET_PQ_event_listen_cancel (eh);
252 eh = NULL;
253 if (NULL != tt)
254 {
255 GNUNET_SCHEDULER_cancel (tt);
256 tt = NULL;
257 }
258}
259
260
261/**
262 * Task called on timeout. Should not happen, means
263 * we did not get the expected event.
264 *
265 * @param cls NULL
266 */
267static void
268timeout_cb (void *cls)
269{
270 ret = 2;
271 GNUNET_break (0);
272 tt = NULL;
273 GNUNET_SCHEDULER_shutdown ();
274}
275
276
277/**
278 * Task called on expected event
279 *
280 * @param cls NULL
281 */
282static void
283event_sched_cb (void *cls,
284 const void *extra,
285 size_t extra_size)
286{
287 GNUNET_assert (5 == extra_size);
288 GNUNET_assert (0 ==
289 memcmp ("hello",
290 extra,
291 5));
292 GNUNET_SCHEDULER_shutdown ();
293}
294
295
296/**
297 * Run tests that need a scheduler.
298 *
299 * @param cls NULL
300 */
301static void
302sched_tests (void *cls)
303{
304 struct GNUNET_DB_EventHeaderP es = {
305 .size = htons (sizeof (es)),
306 .type = htons (42)
307 };
308
309
310 tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
311 &timeout_cb,
312 NULL);
313 eh = GNUNET_PQ_event_listen (db,
314 &es,
315 GNUNET_TIME_UNIT_FOREVER_REL,
316 &event_sched_cb,
317 NULL);
318 GNUNET_PQ_reconnect (db);
319 GNUNET_SCHEDULER_add_shutdown (&event_end,
320 NULL);
321 GNUNET_PQ_event_notify (db,
322 &es,
323 "hello",
324 5);
325}
326
327
328int
329main (int argc,
330 const char *const argv[])
331{
332 struct GNUNET_PQ_ExecuteStatement es[] = {
333 GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS test_pq ("
334 " pub BYTEA NOT NULL"
335 ",sig BYTEA NOT NULL"
336 ",abs_time INT8 NOT NULL"
337 ",forever INT8 NOT NULL"
338 ",hash BYTEA NOT NULL CHECK(LENGTH(hash)=64)"
339 ",vsize VARCHAR NOT NULL"
340 ",u16 INT2 NOT NULL"
341 ",u32 INT4 NOT NULL"
342 ",u64 INT8 NOT NULL"
343 ",unn INT8"
344 ")"),
345 GNUNET_PQ_EXECUTE_STATEMENT_END
346 };
347
348 GNUNET_log_setup ("test-pq",
349 "INFO",
350 NULL);
351 db = GNUNET_PQ_connect ("postgres:///gnunetcheck",
352 NULL,
353 es,
354 NULL);
355 if (NULL == db)
356 {
357 fprintf (stderr,
358 "Cannot run test, database connection failed\n");
359 return 77;
360 }
361 if (CONNECTION_OK != PQstatus (db->conn))
362 {
363 fprintf (stderr,
364 "Cannot run test, database connection failed: %s\n",
365 PQerrorMessage (db->conn));
366 GNUNET_break (0);
367 GNUNET_PQ_disconnect (db);
368 return 77; /* signal test was skipped */
369 }
370 if (GNUNET_OK !=
371 postgres_prepare (db))
372 {
373 GNUNET_break (0);
374 GNUNET_PQ_disconnect (db);
375 return 1;
376 }
377 ret = run_queries (db);
378 if (0 != ret)
379 {
380 GNUNET_break (0);
381 GNUNET_PQ_disconnect (db);
382 return ret;
383 }
384 GNUNET_SCHEDULER_run (&sched_tests,
385 NULL);
386 if (0 != ret)
387 {
388 GNUNET_break (0);
389 GNUNET_PQ_disconnect (db);
390 return ret;
391 }
392#if TEST_RESTART
393 fprintf (stderr, "Please restart Postgres database now!\n");
394 sleep (60);
395 ret |= run_queries (db);
396 fprintf (stderr, "Result: %d (expect: 1 -- if you restarted the DB)\n", ret);
397 ret |= run_queries (db);
398 fprintf (stderr, "Result: %d (expect: 0)\n", ret);
399#endif
400 {
401 struct GNUNET_PQ_ExecuteStatement es[] = {
402 GNUNET_PQ_make_execute ("DROP TABLE test_pq"),
403 GNUNET_PQ_EXECUTE_STATEMENT_END
404 };
405
406 if (GNUNET_OK !=
407 GNUNET_PQ_exec_statements (db,
408 es))
409 {
410 fprintf (stderr,
411 "Failed to drop table\n");
412 GNUNET_PQ_disconnect (db);
413 return 1;
414 }
415 }
416 GNUNET_PQ_disconnect (db);
417 return ret;
418}
419
420
421/* end of test_pq.c */