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.h101
-rw-r--r--src/pq/pq_connect.c543
-rw-r--r--src/pq/pq_eval.c307
-rw-r--r--src/pq/pq_event.c563
-rw-r--r--src/pq/pq_exec.c117
-rw-r--r--src/pq/pq_prepare.c132
-rw-r--r--src/pq/pq_query_helper.c572
-rw-r--r--src/pq/pq_result_helper.c1120
-rw-r--r--src/pq/test_pq.c421
12 files changed, 0 insertions, 4109 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 d3a2ce9ac..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 2: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 9f7e45f62..000000000
--- a/src/pq/pq.h
+++ /dev/null
@@ -1,101 +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 * Suffix to append to path to load on startup.
64 */
65 char *auto_suffix;
66
67 /**
68 * Map managing event subscriptions.
69 */
70 struct GNUNET_CONTAINER_MultiShortmap *channel_map;
71
72 /**
73 * Task responsible for processing events.
74 */
75 struct GNUNET_SCHEDULER_Task *event_task;
76
77 /**
78 * File descriptor wrapper for @e event_task.
79 */
80 struct GNUNET_NETWORK_Handle *rfd;
81
82 /**
83 * Flags controlling the connection.
84 */
85 enum GNUNET_PQ_Options flags;
86};
87
88
89/**
90 * Internal API. Reconnect should re-register notifications
91 * after a disconnect.
92 *
93 * @param db the DB handle
94 * @param fd socket to listen on
95 */
96void
97GNUNET_PQ_event_reconnect_ (struct GNUNET_PQ_Context *db,
98 int fd);
99
100
101#endif
diff --git a/src/pq/pq_connect.c b/src/pq/pq_connect.c
deleted file mode 100644
index 2e36f58f1..000000000
--- a/src/pq/pq_connect.c
+++ /dev/null
@@ -1,543 +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 NULL == load_path
76 ? NULL
77 : "",
78 es,
79 ps,
80 GNUNET_PQ_FLAG_NONE);
81}
82
83
84struct GNUNET_PQ_Context *
85GNUNET_PQ_connect2 (const char *config_str,
86 const char *load_path,
87 const char *auto_suffix,
88 const struct GNUNET_PQ_ExecuteStatement *es,
89 const struct GNUNET_PQ_PreparedStatement *ps,
90 enum GNUNET_PQ_Options flags)
91{
92 struct GNUNET_PQ_Context *db;
93 unsigned int elen = 0;
94 unsigned int plen = 0;
95
96 if (NULL != es)
97 while (NULL != es[elen].sql)
98 elen++;
99 if (NULL != ps)
100 while (NULL != ps[plen].name)
101 plen++;
102
103 db = GNUNET_new (struct GNUNET_PQ_Context);
104 db->flags = flags;
105 db->config_str = GNUNET_strdup (config_str);
106 if (NULL != load_path)
107 db->load_path = GNUNET_strdup (load_path);
108 if (NULL != auto_suffix)
109 db->auto_suffix = GNUNET_strdup (auto_suffix);
110 if (0 != elen)
111 {
112 db->es = GNUNET_new_array (elen + 1,
113 struct GNUNET_PQ_ExecuteStatement);
114 memcpy (db->es,
115 es,
116 elen * sizeof (struct GNUNET_PQ_ExecuteStatement));
117 }
118 if (0 != plen)
119 {
120 db->ps = GNUNET_new_array (plen + 1,
121 struct GNUNET_PQ_PreparedStatement);
122 memcpy (db->ps,
123 ps,
124 plen * sizeof (struct GNUNET_PQ_PreparedStatement));
125 }
126 db->channel_map = GNUNET_CONTAINER_multishortmap_create (16,
127 GNUNET_YES);
128 GNUNET_PQ_reconnect (db);
129 if (NULL == db->conn)
130 {
131 GNUNET_CONTAINER_multishortmap_destroy (db->channel_map);
132 GNUNET_free (db->load_path);
133 GNUNET_free (db->auto_suffix);
134 GNUNET_free (db->config_str);
135 GNUNET_free (db);
136 return NULL;
137 }
138 return db;
139}
140
141
142enum GNUNET_GenericReturnValue
143GNUNET_PQ_exec_sql (struct GNUNET_PQ_Context *db,
144 const char *buf)
145{
146 struct GNUNET_OS_Process *psql;
147 enum GNUNET_OS_ProcessStatusType type;
148 unsigned long code;
149 enum GNUNET_GenericReturnValue ret;
150 char *fn;
151
152 GNUNET_asprintf (&fn,
153 "%s%s.sql",
154 db->load_path,
155 buf);
156 if (GNUNET_YES !=
157 GNUNET_DISK_file_test (fn))
158 {
159 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
160 "SQL resource `%s' does not exist\n",
161 fn);
162 GNUNET_free (fn);
163 return GNUNET_NO;
164 }
165 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
166 "Applying SQL file `%s' on database %s\n",
167 fn,
168 db->config_str);
169 psql = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
170 NULL,
171 NULL,
172 NULL,
173 "psql",
174 "psql",
175 db->config_str,
176 "-f",
177 fn,
178 "-q",
179 "--set",
180 "ON_ERROR_STOP=1",
181 NULL);
182 if (NULL == psql)
183 {
184 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
185 "exec",
186 "psql");
187 GNUNET_free (fn);
188 return GNUNET_SYSERR;
189 }
190 ret = GNUNET_OS_process_wait_status (psql,
191 &type,
192 &code);
193 if (GNUNET_OK != ret)
194 {
195 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
196 "psql on file %s did not finish, killed it!\n",
197 fn);
198 /* can happen if we got a signal, like CTRL-C, before
199 psql was complete */
200 (void) GNUNET_OS_process_kill (psql,
201 SIGKILL);
202 GNUNET_OS_process_destroy (psql);
203 GNUNET_free (fn);
204 return GNUNET_SYSERR;
205 }
206 GNUNET_OS_process_destroy (psql);
207 if ( (GNUNET_OS_PROCESS_EXITED != type) ||
208 (0 != code) )
209 {
210 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
211 "Could not run PSQL on file %s: psql exit code was %d\n",
212 fn,
213 (int) code);
214 GNUNET_free (fn);
215 return GNUNET_SYSERR;
216 }
217 GNUNET_free (fn);
218 return GNUNET_OK;
219}
220
221
222enum GNUNET_GenericReturnValue
223GNUNET_PQ_run_sql (struct GNUNET_PQ_Context *db,
224 const char *load_path)
225{
226 const char *load_path_suffix;
227 size_t slen = strlen (load_path) + 10;
228
229 load_path_suffix = strrchr (load_path, '/');
230 if (NULL == load_path_suffix)
231 load_path_suffix = load_path;
232 else
233 load_path_suffix++; /* skip '/' */
234 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
235 "Loading SQL resources from `%s'\n",
236 load_path);
237 for (unsigned int i = 1; i<10000; i++)
238 {
239 char patch_name[slen];
240 enum GNUNET_DB_QueryStatus qs;
241
242 /* Check with DB versioning schema if this patch was already applied,
243 if so, skip it. */
244 GNUNET_snprintf (patch_name,
245 sizeof (patch_name),
246 "%s%04u",
247 load_path_suffix,
248 i);
249 {
250 char *applied_by;
251 struct GNUNET_PQ_QueryParam params[] = {
252 GNUNET_PQ_query_param_string (patch_name),
253 GNUNET_PQ_query_param_end
254 };
255 struct GNUNET_PQ_ResultSpec rs[] = {
256 GNUNET_PQ_result_spec_string ("applied_by",
257 &applied_by),
258 GNUNET_PQ_result_spec_end
259 };
260
261 qs = GNUNET_PQ_eval_prepared_singleton_select (db,
262 "gnunet_pq_check_patch",
263 params,
264 rs);
265 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
266 {
267 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
268 "Database version %s already applied by %s, skipping\n",
269 patch_name,
270 applied_by);
271 GNUNET_PQ_cleanup_result (rs);
272 }
273 if (GNUNET_DB_STATUS_HARD_ERROR == qs)
274 {
275 GNUNET_break (0);
276 return GNUNET_SYSERR;
277 }
278 }
279 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
280 continue; /* patch already applied, skip it */
281
282 if (0 != (GNUNET_PQ_FLAG_CHECK_CURRENT & db->flags))
283 {
284 /* We are only checking, found unapplied patch, bad! */
285 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
286 "Database outdated, patch %s missing. Aborting!\n",
287 patch_name);
288 return GNUNET_SYSERR;
289 }
290 else
291 {
292 /* patch not yet applied, run it! */
293 enum GNUNET_GenericReturnValue ret;
294
295 GNUNET_snprintf (patch_name,
296 sizeof (patch_name),
297 "%s%04u",
298 load_path,
299 i);
300 ret = GNUNET_PQ_exec_sql (db,
301 patch_name);
302 if (GNUNET_NO == ret)
303 break;
304 if (GNUNET_SYSERR == ret)
305 return GNUNET_SYSERR;
306 }
307 }
308 return GNUNET_OK;
309}
310
311
312void
313GNUNET_PQ_reconnect_if_down (struct GNUNET_PQ_Context *db)
314{
315 if (1 ==
316 PQconsumeInput (db->conn))
317 return;
318 if (CONNECTION_BAD != PQstatus (db->conn))
319 return;
320 GNUNET_PQ_reconnect (db);
321}
322
323
324void
325GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db)
326{
327 GNUNET_PQ_event_reconnect_ (db,
328 -1);
329 if (NULL != db->conn)
330 PQfinish (db->conn);
331 db->conn = PQconnectdb (db->config_str);
332 if ( (NULL == db->conn) ||
333 (CONNECTION_OK != PQstatus (db->conn)) )
334 {
335 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
336 "pq",
337 "Database connection to '%s' failed: %s\n",
338 db->config_str,
339 (NULL != db->conn)
340 ? PQerrorMessage (db->conn)
341 : "PQconnectdb returned NULL");
342 if (NULL != db->conn)
343 {
344 PQfinish (db->conn);
345 db->conn = NULL;
346 }
347 return;
348 }
349 PQsetNoticeReceiver (db->conn,
350 &pq_notice_receiver_cb,
351 db);
352 PQsetNoticeProcessor (db->conn,
353 &pq_notice_processor_cb,
354 db);
355 if (NULL != db->auto_suffix)
356 {
357 PGresult *res;
358
359 res = PQprepare (db->conn,
360 "gnunet_pq_check_patch",
361 "SELECT"
362 " applied_by"
363 " FROM _v.patches"
364 " WHERE patch_name = $1"
365 " LIMIT 1",
366 1,
367 NULL);
368 if (PGRES_COMMAND_OK != PQresultStatus (res))
369 {
370 enum GNUNET_GenericReturnValue ret;
371
372 PQclear (res);
373 if (0 != (db->flags & GNUNET_PQ_FLAG_DROP))
374 {
375 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
376 "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet. Not attempting drop!\n");
377 PQfinish (db->conn);
378 db->conn = NULL;
379 return;
380 }
381 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
382 "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet, loading versioning!\n");
383 ret = GNUNET_PQ_exec_sql (db,
384 "versioning");
385 if (GNUNET_NO == ret)
386 {
387 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
388 "Failed to find SQL file to load database versioning logic\n");
389 PQfinish (db->conn);
390 db->conn = NULL;
391 return;
392 }
393 if (GNUNET_SYSERR == ret)
394 {
395 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
396 "Failed to run SQL logic to setup database versioning logic\n");
397 PQfinish (db->conn);
398 db->conn = NULL;
399 return;
400 }
401 /* try again to prepare our statement! */
402 res = PQprepare (db->conn,
403 "gnunet_pq_check_patch",
404 "SELECT"
405 " applied_by"
406 " FROM _v.patches"
407 " WHERE patch_name = $1"
408 " LIMIT 1",
409 1,
410 NULL);
411 if (PGRES_COMMAND_OK != PQresultStatus (res))
412 {
413 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
414 "Failed to run SQL logic to setup database versioning logic: %s/%s\n",
415 PQresultErrorMessage (res),
416 PQerrorMessage (db->conn));
417 PQclear (res);
418 PQfinish (db->conn);
419 db->conn = NULL;
420 return;
421 }
422 }
423 PQclear (res);
424
425 if (GNUNET_SYSERR ==
426 GNUNET_PQ_run_sql (db,
427 db->auto_suffix))
428 {
429 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
430 "Failed to load SQL statements from `%s*'\n",
431 db->auto_suffix);
432 PQfinish (db->conn);
433 db->conn = NULL;
434 return;
435 }
436 }
437 if ( (NULL != db->es) &&
438 (GNUNET_OK !=
439 GNUNET_PQ_exec_statements (db,
440 db->es)) )
441 {
442 PQfinish (db->conn);
443 db->conn = NULL;
444 return;
445 }
446 if ( (NULL != db->ps) &&
447 (GNUNET_OK !=
448 GNUNET_PQ_prepare_statements (db,
449 db->ps)) )
450 {
451 PQfinish (db->conn);
452 db->conn = NULL;
453 return;
454 }
455 GNUNET_PQ_event_reconnect_ (db,
456 PQsocket (db->conn));
457}
458
459
460struct GNUNET_PQ_Context *
461GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
462 const char *section,
463 const char *load_path_suffix,
464 const struct GNUNET_PQ_ExecuteStatement *es,
465 const struct GNUNET_PQ_PreparedStatement *ps)
466{
467 return GNUNET_PQ_connect_with_cfg2 (cfg,
468 section,
469 load_path_suffix,
470 es,
471 ps,
472 GNUNET_PQ_FLAG_NONE);
473}
474
475
476struct GNUNET_PQ_Context *
477GNUNET_PQ_connect_with_cfg2 (const struct GNUNET_CONFIGURATION_Handle *cfg,
478 const char *section,
479 const char *load_path_suffix,
480 const struct GNUNET_PQ_ExecuteStatement *es,
481 const struct GNUNET_PQ_PreparedStatement *ps,
482 enum GNUNET_PQ_Options flags)
483{
484 struct GNUNET_PQ_Context *db;
485 char *conninfo;
486 char *load_path;
487
488 if (GNUNET_OK !=
489 GNUNET_CONFIGURATION_get_value_string (cfg,
490 section,
491 "CONFIG",
492 &conninfo))
493 conninfo = NULL;
494 load_path = NULL;
495 if (GNUNET_OK !=
496 GNUNET_CONFIGURATION_get_value_filename (cfg,
497 section,
498 "SQL_DIR",
499 &load_path))
500 {
501 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
502 section,
503 "SQL_DIR");
504 }
505 if ( (NULL != load_path_suffix) &&
506 (NULL == load_path) )
507 {
508 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
509 section,
510 "SQL_DIR");
511 return NULL;
512 }
513 db = GNUNET_PQ_connect2 (conninfo == NULL ? "" : conninfo,
514 load_path,
515 load_path_suffix,
516 es,
517 ps,
518 flags);
519 GNUNET_free (load_path);
520 GNUNET_free (conninfo);
521 return db;
522}
523
524
525void
526GNUNET_PQ_disconnect (struct GNUNET_PQ_Context *db)
527{
528 if (NULL == db)
529 return;
530 GNUNET_assert (0 ==
531 GNUNET_CONTAINER_multishortmap_size (db->channel_map));
532 GNUNET_CONTAINER_multishortmap_destroy (db->channel_map);
533 GNUNET_free (db->es);
534 GNUNET_free (db->ps);
535 GNUNET_free (db->load_path);
536 GNUNET_free (db->auto_suffix);
537 GNUNET_free (db->config_str);
538 PQfinish (db->conn);
539 GNUNET_free (db);
540}
541
542
543/* 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 aff52dd5c..000000000
--- a/src/pq/pq_event.c
+++ /dev/null
@@ -1,563 +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 unsigned int cnt = 0;
193
194 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
195 "PG poll job active\n");
196 if (1 !=
197 PQconsumeInput (db->conn))
198 {
199 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
200 "Failed to read from Postgres: %s\n",
201 PQerrorMessage (db->conn));
202 if (CONNECTION_BAD != PQstatus (db->conn))
203 return;
204 GNUNET_PQ_reconnect (db);
205 return;
206 }
207 while (NULL != (n = PQnotifies (db->conn)))
208 {
209 struct GNUNET_ShortHashCode sh;
210 struct NotifyContext ctx = {
211 .extra = NULL
212 };
213
214 cnt++;
215 if ('X' != toupper ((int) n->relname[0]))
216 {
217 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
218 "Ignoring notification for unsupported channel identifier `%s'\n",
219 n->relname);
220 PQfreemem (n);
221 continue;
222 }
223 if (GNUNET_OK !=
224 channel_to_sh (&n->relname[1],
225 &sh))
226 {
227 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
228 "Ignoring notification for unsupported channel identifier `%s'\n",
229 n->relname);
230 PQfreemem (n);
231 continue;
232 }
233 if ( (NULL != n->extra) &&
234 (GNUNET_OK !=
235 GNUNET_STRINGS_string_to_data_alloc (n->extra,
236 strlen (n->extra),
237 &ctx.extra,
238 &ctx.extra_size)))
239 {
240 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
241 "Ignoring notification for unsupported extra data `%s' on channel `%s'\n",
242 n->extra,
243 n->relname);
244 PQfreemem (n);
245 continue;
246 }
247 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
248 "Received notification %s with extra data `%.*s'\n",
249 n->relname,
250 (int) ctx.extra_size,
251 (const char *) ctx.extra);
252 GNUNET_CONTAINER_multishortmap_get_multiple (db->channel_map,
253 &sh,
254 &do_notify,
255 &ctx);
256 GNUNET_free (ctx.extra);
257 PQfreemem (n);
258 }
259 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
260 "PG poll job finishes after %u events\n",
261 cnt);
262}
263
264
265/**
266 * The GNUnet scheduler notifies us that we need to
267 * trigger the DB event poller.
268 *
269 * @param cls a `struct GNUNET_PQ_Context *`
270 */
271static void
272do_scheduler_notify (void *cls)
273{
274 struct GNUNET_PQ_Context *db = cls;
275
276 db->event_task = NULL;
277 if (NULL == db->rfd)
278 GNUNET_PQ_reconnect (db);
279 event_do_poll (db);
280 if (NULL != db->event_task)
281 return;
282 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
283 "Resubscribing\n");
284 if (NULL == db->rfd)
285 {
286 db->event_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
287 &do_scheduler_notify,
288 db);
289 return;
290 }
291 db->event_task
292 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
293 db->rfd,
294 &do_scheduler_notify,
295 db);
296}
297
298
299/**
300 * Function called when the Postgres FD changes and we need
301 * to update the scheduler event loop task.
302 *
303 * @param cls a `struct GNUNET_PQ_Context *`
304 * @param fd the file descriptor, possibly -1
305 */
306static void
307scheduler_fd_cb (void *cls,
308 int fd)
309{
310 struct GNUNET_PQ_Context *db = cls;
311
312 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
313 "New poll FD is %d\n",
314 fd);
315 if (NULL != db->event_task)
316 {
317 GNUNET_SCHEDULER_cancel (db->event_task);
318 db->event_task = NULL;
319 }
320 GNUNET_free (db->rfd);
321 if (-1 == fd)
322 return;
323 if (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map))
324 return;
325 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
326 "Activating poll job on %d\n",
327 fd);
328 db->rfd = GNUNET_NETWORK_socket_box_native (fd);
329 db->event_task
330 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_ZERO,
331 db->rfd,
332 &do_scheduler_notify,
333 db);
334}
335
336
337/**
338 * Helper function to trigger an SQL @a cmd on @a db
339 *
340 * @param db database to send command to
341 * @param cmd prefix of the command to send
342 * @param eh details about the event
343 */
344static void
345manage_subscribe (struct GNUNET_PQ_Context *db,
346 const char *cmd,
347 struct GNUNET_DB_EventHandler *eh)
348{
349 char sql[16 + 64];
350 char *end;
351 PGresult *result;
352
353 if (NULL == db->conn)
354 return;
355 end = stpcpy (sql,
356 cmd);
357 end = sh_to_channel (&eh->sh,
358 end);
359 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
360 "Executing PQ command `%s'\n",
361 sql);
362 result = PQexec (db->conn,
363 sql);
364 if (PGRES_COMMAND_OK != PQresultStatus (result))
365 {
366 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
367 "pq",
368 "Failed to execute `%s': %s/%s/%s/%s/%s",
369 sql,
370 PQresultErrorField (result,
371 PG_DIAG_MESSAGE_PRIMARY),
372 PQresultErrorField (result,
373 PG_DIAG_MESSAGE_DETAIL),
374 PQresultErrorMessage (result),
375 PQresStatus (PQresultStatus (result)),
376 PQerrorMessage (db->conn));
377 }
378 PQclear (result);
379}
380
381
382/**
383 * Re-subscribe to notifications after disconnect.
384 *
385 * @param cls the DB context
386 * @param sh the short hash of the channel
387 * @param eh the event handler
388 * @return #GNUNET_OK to continue to iterate
389 */
390static int
391register_notify (void *cls,
392 const struct GNUNET_ShortHashCode *sh,
393 void *value)
394{
395 struct GNUNET_PQ_Context *db = cls;
396 struct GNUNET_DB_EventHandler *eh = value;
397
398 manage_subscribe (db,
399 "LISTEN X",
400 eh);
401 return GNUNET_OK;
402}
403
404
405void
406GNUNET_PQ_event_reconnect_ (struct GNUNET_PQ_Context *db,
407 int fd)
408{
409 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
410 "Change in PQ event FD to %d\n",
411 fd);
412 scheduler_fd_cb (db,
413 fd);
414 GNUNET_CONTAINER_multishortmap_iterate (db->channel_map,
415 &register_notify,
416 db);
417}
418
419
420/**
421 * Function run on timeout for an event. Triggers
422 * the notification, but does NOT clear the handler.
423 *
424 * @param cls a `struct GNUNET_DB_EventHandler *`
425 */
426static void
427event_timeout (void *cls)
428{
429 struct GNUNET_DB_EventHandler *eh = cls;
430
431 eh->timeout_task = NULL;
432 eh->cb (eh->cb_cls,
433 NULL,
434 0);
435}
436
437
438struct GNUNET_DB_EventHandler *
439GNUNET_PQ_event_listen (struct GNUNET_PQ_Context *db,
440 const struct GNUNET_DB_EventHeaderP *es,
441 struct GNUNET_TIME_Relative timeout,
442 GNUNET_DB_EventCallback cb,
443 void *cb_cls)
444{
445 struct GNUNET_DB_EventHandler *eh;
446 bool sub;
447
448 eh = GNUNET_new (struct GNUNET_DB_EventHandler);
449 eh->db = db;
450 es_to_sh (es,
451 &eh->sh);
452 eh->cb = cb;
453 eh->cb_cls = cb_cls;
454 sub = (NULL ==
455 GNUNET_CONTAINER_multishortmap_get (db->channel_map,
456 &eh->sh));
457 GNUNET_assert (GNUNET_OK ==
458 GNUNET_CONTAINER_multishortmap_put (db->channel_map,
459 &eh->sh,
460 eh,
461 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
462 if (NULL == db->event_task)
463 {
464 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
465 "Starting event scheduler\n");
466 scheduler_fd_cb (db,
467 PQsocket (db->conn));
468 }
469 if (sub)
470 manage_subscribe (db,
471 "LISTEN X",
472 eh);
473 eh->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout,
474 &event_timeout,
475 eh);
476 return eh;
477}
478
479
480void
481GNUNET_PQ_event_listen_cancel (struct GNUNET_DB_EventHandler *eh)
482{
483 struct GNUNET_PQ_Context *db = eh->db;
484
485 GNUNET_assert (GNUNET_OK ==
486 GNUNET_CONTAINER_multishortmap_remove (db->channel_map,
487 &eh->sh,
488 eh));
489 if (NULL ==
490 GNUNET_CONTAINER_multishortmap_get (db->channel_map,
491 &eh->sh))
492 manage_subscribe (db,
493 "UNLISTEN X",
494 eh);
495 if (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map))
496 {
497 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
498 "Stopping PQ event scheduler job\n");
499 GNUNET_free (db->rfd);
500 if (NULL != db->event_task)
501 {
502 GNUNET_SCHEDULER_cancel (db->event_task);
503 db->event_task = NULL;
504 }
505 }
506 if (NULL != eh->timeout_task)
507 {
508 GNUNET_SCHEDULER_cancel (eh->timeout_task);
509 eh->timeout_task = NULL;
510 }
511 GNUNET_free (eh);
512}
513
514
515void
516GNUNET_PQ_event_notify (struct GNUNET_PQ_Context *db,
517 const struct GNUNET_DB_EventHeaderP *es,
518 const void *extra,
519 size_t extra_size)
520{
521 char sql[16 + 64 + extra_size * 8 / 5 + 8];
522 char *end;
523 PGresult *result;
524
525 end = stpcpy (sql,
526 "NOTIFY X");
527 end = es_to_channel (es,
528 end);
529 end = stpcpy (end,
530 ", '");
531 end = GNUNET_STRINGS_data_to_string (extra,
532 extra_size,
533 end,
534 sizeof (sql) - (end - sql) - 1);
535 GNUNET_assert (NULL != end);
536 *end = '\0';
537 end = stpcpy (end,
538 "'");
539 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
540 "Executing command `%s'\n",
541 sql);
542 result = PQexec (db->conn,
543 sql);
544 if (PGRES_COMMAND_OK != PQresultStatus (result))
545 {
546 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
547 "pq",
548 "Failed to execute `%s': %s/%s/%s/%s/%s",
549 sql,
550 PQresultErrorField (result,
551 PG_DIAG_MESSAGE_PRIMARY),
552 PQresultErrorField (result,
553 PG_DIAG_MESSAGE_DETAIL),
554 PQresultErrorMessage (result),
555 PQresStatus (PQresultStatus (result)),
556 PQerrorMessage (db->conn));
557 }
558 PQclear (result);
559 event_do_poll (db);
560}
561
562
563/* 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 fcf1cc793..000000000
--- a/src/pq/pq_prepare.c
+++ /dev/null
@@ -1,132 +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 ret = PQdescribePrepared (db->conn,
115 ps[i].name);
116 if (PGRES_COMMAND_OK != PQresultStatus (ret))
117 {
118 PQclear (ret);
119 return GNUNET_SYSERR;
120 }
121 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
122 "pq",
123 "Statement `%s' already known. Ignoring the issue in the hope that you are using connection pooling...\n",
124 ps[i].name);
125 }
126 PQclear (ret);
127 }
128 return GNUNET_OK;
129}
130
131
132/* 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 ce8ce8f87..000000000
--- a/src/pq/pq_query_helper.c
+++ /dev/null
@@ -1,572 +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 .conv = &qconv_null,
74 .num_params = 1
75 };
76
77 return res;
78}
79
80
81/**
82 * Function called to convert input argument into SQL parameters.
83 *
84 * @param cls closure
85 * @param data pointer to input argument
86 * @param data_len number of bytes in @a data (if applicable)
87 * @param[out] param_values SQL data to set
88 * @param[out] param_lengths SQL length data to set
89 * @param[out] param_formats SQL format data to set
90 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
91 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
92 * @param scratch_length number of entries left in @a scratch
93 * @return -1 on error, number of offsets used in @a scratch otherwise
94 */
95static int
96qconv_fixed (void *cls,
97 const void *data,
98 size_t data_len,
99 void *param_values[],
100 int param_lengths[],
101 int param_formats[],
102 unsigned int param_length,
103 void *scratch[],
104 unsigned int scratch_length)
105{
106 (void) scratch;
107 (void) scratch_length;
108 GNUNET_break (NULL == cls);
109 if (1 != param_length)
110 return -1;
111 param_values[0] = (void *) data;
112 param_lengths[0] = data_len;
113 param_formats[0] = 1;
114 return 0;
115}
116
117
118struct GNUNET_PQ_QueryParam
119GNUNET_PQ_query_param_fixed_size (const void *ptr,
120 size_t ptr_size)
121{
122 struct GNUNET_PQ_QueryParam res = {
123 &qconv_fixed, NULL, ptr, ptr_size, 1
124 };
125
126 return res;
127}
128
129
130struct GNUNET_PQ_QueryParam
131GNUNET_PQ_query_param_string (const char *ptr)
132{
133 return GNUNET_PQ_query_param_fixed_size (ptr,
134 strlen (ptr));
135}
136
137
138struct GNUNET_PQ_QueryParam
139GNUNET_PQ_query_param_bool (bool b)
140{
141 static uint8_t bt = 1;
142 static uint8_t bf = 0;
143
144 return GNUNET_PQ_query_param_fixed_size (b ? &bt : &bf,
145 sizeof (uint8_t));
146}
147
148
149/**
150 * Function called to convert input argument into SQL parameters.
151 *
152 * @param cls closure
153 * @param data pointer to input argument
154 * @param data_len number of bytes in @a data (if applicable)
155 * @param[out] param_values SQL data to set
156 * @param[out] param_lengths SQL length data to set
157 * @param[out] param_formats SQL format data to set
158 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
159 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
160 * @param scratch_length number of entries left in @a scratch
161 * @return -1 on error, number of offsets used in @a scratch otherwise
162 */
163static int
164qconv_uint16 (void *cls,
165 const void *data,
166 size_t data_len,
167 void *param_values[],
168 int param_lengths[],
169 int param_formats[],
170 unsigned int param_length,
171 void *scratch[],
172 unsigned int scratch_length)
173{
174 const uint16_t *u_hbo = data;
175 uint16_t *u_nbo;
176
177 (void) scratch;
178 (void) scratch_length;
179 GNUNET_break (NULL == cls);
180 if (1 != param_length)
181 return -1;
182 u_nbo = GNUNET_new (uint16_t);
183 scratch[0] = u_nbo;
184 *u_nbo = htons (*u_hbo);
185 param_values[0] = (void *) u_nbo;
186 param_lengths[0] = sizeof(uint16_t);
187 param_formats[0] = 1;
188 return 1;
189}
190
191
192struct GNUNET_PQ_QueryParam
193GNUNET_PQ_query_param_uint16 (const uint16_t *x)
194{
195 struct GNUNET_PQ_QueryParam res = {
196 .conv = &qconv_uint16,
197 .data = x,
198 .size = sizeof(*x),
199 .num_params = 1
200 };
201
202 return res;
203}
204
205
206/**
207 * Function called to convert input argument into SQL parameters.
208 *
209 * @param cls closure
210 * @param data pointer to input argument
211 * @param data_len number of bytes in @a data (if applicable)
212 * @param[out] param_values SQL data to set
213 * @param[out] param_lengths SQL length data to set
214 * @param[out] param_formats SQL format data to set
215 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
216 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
217 * @param scratch_length number of entries left in @a scratch
218 * @return -1 on error, number of offsets used in @a scratch otherwise
219 */
220static int
221qconv_uint32 (void *cls,
222 const void *data,
223 size_t data_len,
224 void *param_values[],
225 int param_lengths[],
226 int param_formats[],
227 unsigned int param_length,
228 void *scratch[],
229 unsigned int scratch_length)
230{
231 const uint32_t *u_hbo = data;
232 uint32_t *u_nbo;
233
234 (void) scratch;
235 (void) scratch_length;
236 GNUNET_break (NULL == cls);
237 if (1 != param_length)
238 return -1;
239 u_nbo = GNUNET_new (uint32_t);
240 scratch[0] = u_nbo;
241 *u_nbo = htonl (*u_hbo);
242 param_values[0] = (void *) u_nbo;
243 param_lengths[0] = sizeof(uint32_t);
244 param_formats[0] = 1;
245 return 1;
246}
247
248
249struct GNUNET_PQ_QueryParam
250GNUNET_PQ_query_param_uint32 (const uint32_t *x)
251{
252 struct GNUNET_PQ_QueryParam res = {
253 .conv = &qconv_uint32,
254 .data = x,
255 .size = sizeof(*x),
256 .num_params = 1
257 };
258
259 return res;
260}
261
262
263/**
264 * Function called to convert input argument into SQL parameters.
265 *
266 * @param cls closure
267 * @param data pointer to input argument
268 * @param data_len number of bytes in @a data (if applicable)
269 * @param[out] param_values SQL data to set
270 * @param[out] param_lengths SQL length data to set
271 * @param[out] param_formats SQL format data to set
272 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
273 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
274 * @param scratch_length number of entries left in @a scratch
275 * @return -1 on error, number of offsets used in @a scratch otherwise
276 */
277static int
278qconv_uint64 (void *cls,
279 const void *data,
280 size_t data_len,
281 void *param_values[],
282 int param_lengths[],
283 int param_formats[],
284 unsigned int param_length,
285 void *scratch[],
286 unsigned int scratch_length)
287{
288 const uint64_t *u_hbo = data;
289 uint64_t *u_nbo;
290
291 (void) scratch;
292 (void) scratch_length;
293 GNUNET_break (NULL == cls);
294 if (1 != param_length)
295 return -1;
296 u_nbo = GNUNET_new (uint64_t);
297 scratch[0] = u_nbo;
298 *u_nbo = GNUNET_htonll (*u_hbo);
299 param_values[0] = (void *) u_nbo;
300 param_lengths[0] = sizeof(uint64_t);
301 param_formats[0] = 1;
302 return 1;
303}
304
305
306struct GNUNET_PQ_QueryParam
307GNUNET_PQ_query_param_uint64 (const uint64_t *x)
308{
309 struct GNUNET_PQ_QueryParam res = {
310 .conv = &qconv_uint64,
311 .data = x,
312 .size = sizeof(*x),
313 .num_params = 1
314 };
315
316 return res;
317}
318
319
320/**
321 * Function called to convert input argument into SQL parameters.
322 *
323 * @param cls closure
324 * @param data pointer to input argument
325 * @param data_len number of bytes in @a data (if applicable)
326 * @param[out] param_values SQL data to set
327 * @param[out] param_lengths SQL length data to set
328 * @param[out] param_formats SQL format data to set
329 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
330 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
331 * @param scratch_length number of entries left in @a scratch
332 * @return -1 on error, number of offsets used in @a scratch otherwise
333 */
334static int
335qconv_rsa_public_key (void *cls,
336 const void *data,
337 size_t data_len,
338 void *param_values[],
339 int param_lengths[],
340 int param_formats[],
341 unsigned int param_length,
342 void *scratch[],
343 unsigned int scratch_length)
344{
345 const struct GNUNET_CRYPTO_RsaPublicKey *rsa = data;
346 void *buf;
347 size_t buf_size;
348
349 GNUNET_break (NULL == cls);
350 if (1 != param_length)
351 return -1;
352 buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rsa,
353 &buf);
354 scratch[0] = buf;
355 param_values[0] = (void *) buf;
356 param_lengths[0] = buf_size;
357 param_formats[0] = 1;
358 return 1;
359}
360
361
362struct GNUNET_PQ_QueryParam
363GNUNET_PQ_query_param_rsa_public_key (
364 const struct GNUNET_CRYPTO_RsaPublicKey *x)
365{
366 struct GNUNET_PQ_QueryParam res = {
367 .conv = &qconv_rsa_public_key,
368 .data = x,
369 .num_params = 1
370 };
371
372 return res;
373}
374
375
376/**
377 * Function called to convert input argument into SQL parameters.
378 *
379 * @param cls closure
380 * @param data pointer to input argument
381 * @param data_len number of bytes in @a data (if applicable)
382 * @param[out] param_values SQL data to set
383 * @param[out] param_lengths SQL length data to set
384 * @param[out] param_formats SQL format data to set
385 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
386 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
387 * @param scratch_length number of entries left in @a scratch
388 * @return -1 on error, number of offsets used in @a scratch otherwise
389 */
390static int
391qconv_rsa_signature (void *cls,
392 const void *data,
393 size_t data_len,
394 void *param_values[],
395 int param_lengths[],
396 int param_formats[],
397 unsigned int param_length,
398 void *scratch[],
399 unsigned int scratch_length)
400{
401 const struct GNUNET_CRYPTO_RsaSignature *sig = data;
402 void *buf;
403 size_t buf_size;
404
405 GNUNET_break (NULL == cls);
406 if (1 != param_length)
407 return -1;
408 buf_size = GNUNET_CRYPTO_rsa_signature_encode (sig,
409 &buf);
410 scratch[0] = buf;
411 param_values[0] = (void *) buf;
412 param_lengths[0] = buf_size;
413 param_formats[0] = 1;
414 return 1;
415}
416
417
418struct GNUNET_PQ_QueryParam
419GNUNET_PQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_RsaSignature *x)
420{
421 struct GNUNET_PQ_QueryParam res = {
422 .conv = &qconv_rsa_signature,
423 .data = x,
424 .num_params = 1
425 };
426
427 return res;
428}
429
430
431/**
432 * Function called to convert input argument into SQL parameters.
433 *
434 * @param cls closure
435 * @param data pointer to input argument
436 * @param data_len number of bytes in @a data (if applicable)
437 * @param[out] param_values SQL data to set
438 * @param[out] param_lengths SQL length data to set
439 * @param[out] param_formats SQL format data to set
440 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
441 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
442 * @param scratch_length number of entries left in @a scratch
443 * @return -1 on error, number of offsets used in @a scratch otherwise
444 */
445static int
446qconv_rel_time (void *cls,
447 const void *data,
448 size_t data_len,
449 void *param_values[],
450 int param_lengths[],
451 int param_formats[],
452 unsigned int param_length,
453 void *scratch[],
454 unsigned int scratch_length)
455{
456 const struct GNUNET_TIME_Relative *u = data;
457 struct GNUNET_TIME_Relative rel;
458 uint64_t *u_nbo;
459
460 GNUNET_break (NULL == cls);
461 if (1 != param_length)
462 return -1;
463 rel = *u;
464 if (rel.rel_value_us > INT64_MAX)
465 rel.rel_value_us = INT64_MAX;
466 u_nbo = GNUNET_new (uint64_t);
467 scratch[0] = u_nbo;
468 *u_nbo = GNUNET_htonll (rel.rel_value_us);
469 param_values[0] = (void *) u_nbo;
470 param_lengths[0] = sizeof(uint64_t);
471 param_formats[0] = 1;
472 return 1;
473}
474
475
476struct GNUNET_PQ_QueryParam
477GNUNET_PQ_query_param_relative_time (const struct GNUNET_TIME_Relative *x)
478{
479 struct GNUNET_PQ_QueryParam res = {
480 .conv = &qconv_rel_time,
481 .data = x,
482 .size = sizeof(*x),
483 .num_params = 1
484 };
485
486 return res;
487}
488
489
490/**
491 * Function called to convert input argument into SQL parameters.
492 *
493 * @param cls closure
494 * @param data pointer to input argument
495 * @param data_len number of bytes in @a data (if applicable)
496 * @param[out] param_values SQL data to set
497 * @param[out] param_lengths SQL length data to set
498 * @param[out] param_formats SQL format data to set
499 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
500 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
501 * @param scratch_length number of entries left in @a scratch
502 * @return -1 on error, number of offsets used in @a scratch otherwise
503 */
504static int
505qconv_abs_time (void *cls,
506 const void *data,
507 size_t data_len,
508 void *param_values[],
509 int param_lengths[],
510 int param_formats[],
511 unsigned int param_length,
512 void *scratch[],
513 unsigned int scratch_length)
514{
515 const struct GNUNET_TIME_Absolute *u = data;
516 struct GNUNET_TIME_Absolute abs;
517 uint64_t *u_nbo;
518
519 GNUNET_break (NULL == cls);
520 if (1 != param_length)
521 return -1;
522 abs = *u;
523 if (abs.abs_value_us > INT64_MAX)
524 abs.abs_value_us = INT64_MAX;
525 u_nbo = GNUNET_new (uint64_t);
526 scratch[0] = u_nbo;
527 *u_nbo = GNUNET_htonll (abs.abs_value_us);
528 param_values[0] = (void *) u_nbo;
529 param_lengths[0] = sizeof(uint64_t);
530 param_formats[0] = 1;
531 return 1;
532}
533
534
535struct GNUNET_PQ_QueryParam
536GNUNET_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x)
537{
538 struct GNUNET_PQ_QueryParam res = {
539 .conv = &qconv_abs_time,
540 .data = x,
541 .size = sizeof(*x),
542 .num_params = 1
543 };
544
545 return res;
546}
547
548
549struct GNUNET_PQ_QueryParam
550GNUNET_PQ_query_param_absolute_time_nbo (
551 const struct GNUNET_TIME_AbsoluteNBO *x)
552{
553 return GNUNET_PQ_query_param_auto_from_type (&x->abs_value_us__);
554}
555
556
557struct GNUNET_PQ_QueryParam
558GNUNET_PQ_query_param_timestamp (const struct GNUNET_TIME_Timestamp *x)
559{
560 return GNUNET_PQ_query_param_absolute_time (&x->abs_time);
561}
562
563
564struct GNUNET_PQ_QueryParam
565GNUNET_PQ_query_param_timestamp_nbo (
566 const struct GNUNET_TIME_TimestampNBO *x)
567{
568 return GNUNET_PQ_query_param_absolute_time_nbo (&x->abs_time_nbo);
569}
570
571
572/* 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 f3d246c36..000000000
--- a/src/pq/pq_result_helper.c
+++ /dev/null
@@ -1,1120 +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 .conv = &extract_string,
501 .cleaner = &clean_string,
502 .dst = (void *) dst,
503 .fname = (name)
504 };
505
506 return res;
507}
508
509
510/**
511 * Extract data from a Postgres database @a result at row @a row.
512 *
513 * @param cls closure
514 * @param result where to extract data from
515 * @param int row to extract data from
516 * @param fname name (or prefix) of the fields to extract from
517 * @param[in,out] dst_size where to store size of result, may be NULL
518 * @param[out] dst where to store the result
519 * @return
520 * #GNUNET_YES if all results could be extracted
521 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
522 */
523static enum GNUNET_GenericReturnValue
524extract_bool (void *cls,
525 PGresult *result,
526 int row,
527 const char *fname,
528 size_t *dst_size,
529 void *dst)
530{
531 bool *b = dst;
532 const uint8_t *res;
533 int fnum;
534 size_t len;
535
536 (void) cls;
537 fnum = PQfnumber (result,
538 fname);
539 if (fnum < 0)
540 {
541 GNUNET_break (0);
542 return GNUNET_SYSERR;
543 }
544 if (PQgetisnull (result,
545 row,
546 fnum))
547 return GNUNET_NO;
548 /* if a field is null, continue but
549 * remember that we now return a different result */
550 len = PQgetlength (result,
551 row,
552 fnum);
553 if (sizeof (uint8_t) != len)
554 {
555 GNUNET_break (0);
556 return GNUNET_SYSERR;
557 }
558 res = (const uint8_t *) PQgetvalue (result,
559 row,
560 fnum);
561 *b = (0 != *res);
562 return GNUNET_OK;
563}
564
565
566struct GNUNET_PQ_ResultSpec
567GNUNET_PQ_result_spec_bool (const char *name,
568 bool *dst)
569{
570 struct GNUNET_PQ_ResultSpec res = {
571 .conv = &extract_bool,
572 .dst = (void *) dst,
573 .fname = name
574 };
575
576 return res;
577}
578
579
580/**
581 * Extract data from a Postgres database @a result at row @a row.
582 *
583 * @param cls closure
584 * @param result where to extract data from
585 * @param int row to extract data from
586 * @param fname name (or prefix) of the fields to extract from
587 * @param[in,out] dst_size where to store size of result, may be NULL
588 * @param[out] dst where to store the result
589 * @return
590 * #GNUNET_YES if all results could be extracted
591 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
592 */
593static enum GNUNET_GenericReturnValue
594extract_rel_time (void *cls,
595 PGresult *result,
596 int row,
597 const char *fname,
598 size_t *dst_size,
599 void *dst)
600{
601 struct GNUNET_TIME_Relative *udst = dst;
602 const int64_t *res;
603 int fnum;
604
605 (void) cls;
606 fnum = PQfnumber (result,
607 fname);
608 if (fnum < 0)
609 {
610 GNUNET_break (0);
611 return GNUNET_SYSERR;
612 }
613 if (PQgetisnull (result,
614 row,
615 fnum))
616 return GNUNET_NO;
617 GNUNET_assert (NULL != dst);
618 if (sizeof(struct GNUNET_TIME_Relative) != *dst_size)
619 {
620 GNUNET_break (0);
621 return GNUNET_SYSERR;
622 }
623 if (sizeof(int64_t) !=
624 PQgetlength (result,
625 row,
626 fnum))
627 {
628 GNUNET_break (0);
629 return GNUNET_SYSERR;
630 }
631 res = (int64_t *) PQgetvalue (result,
632 row,
633 fnum);
634 if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res))
635 *udst = GNUNET_TIME_UNIT_FOREVER_REL;
636 else
637 udst->rel_value_us = GNUNET_ntohll ((uint64_t) *res);
638 return GNUNET_OK;
639}
640
641
642struct GNUNET_PQ_ResultSpec
643GNUNET_PQ_result_spec_relative_time (const char *name,
644 struct GNUNET_TIME_Relative *rt)
645{
646 struct GNUNET_PQ_ResultSpec res = {
647 .conv = &extract_rel_time,
648 .dst = (void *) rt,
649 .dst_size = sizeof(*rt),
650 .fname = name
651 };
652
653 return res;
654}
655
656
657/**
658 * Extract data from a Postgres database @a result at row @a row.
659 *
660 * @param cls closure
661 * @param result where to extract data from
662 * @param int row to extract data from
663 * @param fname name (or prefix) of the fields to extract from
664 * @param[in,out] dst_size where to store size of result, may be NULL
665 * @param[out] dst where to store the result
666 * @return
667 * #GNUNET_YES if all results could be extracted
668 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
669 */
670static enum GNUNET_GenericReturnValue
671extract_abs_time (void *cls,
672 PGresult *result,
673 int row,
674 const char *fname,
675 size_t *dst_size,
676 void *dst)
677{
678 struct GNUNET_TIME_Absolute *udst = dst;
679 const int64_t *res;
680 int fnum;
681
682 (void) cls;
683 fnum = PQfnumber (result,
684 fname);
685 if (fnum < 0)
686 {
687 GNUNET_break (0);
688 return GNUNET_SYSERR;
689 }
690 if (PQgetisnull (result,
691 row,
692 fnum))
693 return GNUNET_NO;
694 GNUNET_assert (NULL != dst);
695 if (sizeof(struct GNUNET_TIME_Absolute) != *dst_size)
696 {
697 GNUNET_break (0);
698 return GNUNET_SYSERR;
699 }
700 if (sizeof(int64_t) !=
701 PQgetlength (result,
702 row,
703 fnum))
704 {
705 GNUNET_break (0);
706 return GNUNET_SYSERR;
707 }
708 res = (int64_t *) PQgetvalue (result,
709 row,
710 fnum);
711 if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res))
712 *udst = GNUNET_TIME_UNIT_FOREVER_ABS;
713 else
714 udst->abs_value_us = GNUNET_ntohll ((uint64_t) *res);
715 return GNUNET_OK;
716}
717
718
719struct GNUNET_PQ_ResultSpec
720GNUNET_PQ_result_spec_absolute_time (const char *name,
721 struct GNUNET_TIME_Absolute *at)
722{
723 struct GNUNET_PQ_ResultSpec res = {
724 .conv = &extract_abs_time,
725 .dst = (void *) at,
726 .dst_size = sizeof(*at),
727 .fname = name
728 };
729
730 return res;
731}
732
733
734struct GNUNET_PQ_ResultSpec
735GNUNET_PQ_result_spec_absolute_time_nbo (const char *name,
736 struct GNUNET_TIME_AbsoluteNBO *at)
737{
738 struct GNUNET_PQ_ResultSpec res =
739 GNUNET_PQ_result_spec_auto_from_type (name,
740 &at->abs_value_us__);
741
742 return res;
743}
744
745
746/**
747 * Extract data from a Postgres database @a result at row @a row.
748 *
749 * @param cls closure
750 * @param result where to extract data from
751 * @param int row to extract data from
752 * @param fname name (or prefix) of the fields to extract from
753 * @param[in,out] dst_size where to store size of result, may be NULL
754 * @param[out] dst where to store the result
755 * @return
756 * #GNUNET_YES if all results could be extracted
757 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
758 */
759static enum GNUNET_GenericReturnValue
760extract_timestamp (void *cls,
761 PGresult *result,
762 int row,
763 const char *fname,
764 size_t *dst_size,
765 void *dst)
766{
767 struct GNUNET_TIME_Timestamp *udst = dst;
768 struct GNUNET_TIME_Absolute abs;
769 const int64_t *res;
770 int fnum;
771
772 (void) cls;
773 fnum = PQfnumber (result,
774 fname);
775 if (fnum < 0)
776 {
777 GNUNET_break (0);
778 return GNUNET_SYSERR;
779 }
780 if (PQgetisnull (result,
781 row,
782 fnum))
783 return GNUNET_NO;
784 GNUNET_assert (NULL != dst);
785 if (sizeof(struct GNUNET_TIME_Absolute) != *dst_size)
786 {
787 GNUNET_break (0);
788 return GNUNET_SYSERR;
789 }
790 if (sizeof(int64_t) !=
791 PQgetlength (result,
792 row,
793 fnum))
794 {
795 GNUNET_break (0);
796 return GNUNET_SYSERR;
797 }
798 res = (int64_t *) PQgetvalue (result,
799 row,
800 fnum);
801 if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res))
802 {
803 abs = GNUNET_TIME_UNIT_FOREVER_ABS;
804 }
805 else
806 {
807 abs.abs_value_us = GNUNET_ntohll ((uint64_t) *res);
808 if (0 != abs.abs_value_us % GNUNET_TIME_UNIT_SECONDS.rel_value_us)
809 {
810 /* timestamps must be multiple of seconds! */
811 GNUNET_break (0);
812 return GNUNET_SYSERR;
813 }
814 }
815 udst->abs_time = abs;
816 return GNUNET_OK;
817}
818
819
820struct GNUNET_PQ_ResultSpec
821GNUNET_PQ_result_spec_timestamp (const char *name,
822 struct GNUNET_TIME_Timestamp *at)
823{
824 struct GNUNET_PQ_ResultSpec res = {
825 .conv = &extract_timestamp,
826 .dst = (void *) at,
827 .dst_size = sizeof(*at),
828 .fname = name
829 };
830
831 return res;
832}
833
834
835/**
836 * Extract data from a Postgres database @a result at row @a row.
837 *
838 * @param cls closure
839 * @param result where to extract data from
840 * @param int row to extract data from
841 * @param fname name (or prefix) of the fields to extract from
842 * @param[in,out] dst_size where to store size of result, may be NULL
843 * @param[out] dst where to store the result
844 * @return
845 * #GNUNET_YES if all results could be extracted
846 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
847 */
848static enum GNUNET_GenericReturnValue
849extract_timestamp_nbo (void *cls,
850 PGresult *result,
851 int row,
852 const char *fname,
853 size_t *dst_size,
854 void *dst)
855{
856 struct GNUNET_TIME_TimestampNBO *udst = dst;
857 struct GNUNET_TIME_Timestamp t;
858 enum GNUNET_GenericReturnValue r;
859
860 r = extract_timestamp (NULL,
861 result,
862 row,
863 fname,
864 dst_size,
865 &t);
866 if (GNUNET_OK != r)
867 return r;
868 *udst = GNUNET_TIME_timestamp_hton (t);
869 return r;
870}
871
872
873struct GNUNET_PQ_ResultSpec
874GNUNET_PQ_result_spec_timestamp_nbo (const char *name,
875 struct GNUNET_TIME_TimestampNBO *at)
876{
877 struct GNUNET_PQ_ResultSpec res = {
878 .conv = &extract_timestamp_nbo,
879 .dst = (void *) at,
880 .dst_size = sizeof(*at),
881 .fname = name
882 };
883
884 return res;
885}
886
887
888/**
889 * Extract data from a Postgres database @a result at row @a row.
890 *
891 * @param cls closure
892 * @param result where to extract data from
893 * @param int row to extract data from
894 * @param fname name (or prefix) of the fields to extract from
895 * @param[in,out] dst_size where to store size of result, may be NULL
896 * @param[out] dst where to store the result
897 * @return
898 * #GNUNET_YES if all results could be extracted
899 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
900 */
901static enum GNUNET_GenericReturnValue
902extract_uint16 (void *cls,
903 PGresult *result,
904 int row,
905 const char *fname,
906 size_t *dst_size,
907 void *dst)
908{
909 uint16_t *udst = dst;
910 const uint16_t *res;
911 int fnum;
912
913 (void) cls;
914 fnum = PQfnumber (result,
915 fname);
916 if (fnum < 0)
917 {
918 GNUNET_break (0);
919 return GNUNET_SYSERR;
920 }
921 if (PQgetisnull (result,
922 row,
923 fnum))
924 return GNUNET_NO;
925 GNUNET_assert (NULL != dst);
926 if (sizeof(uint16_t) != *dst_size)
927 {
928 GNUNET_break (0);
929 return GNUNET_SYSERR;
930 }
931 if (sizeof(uint16_t) !=
932 PQgetlength (result,
933 row,
934 fnum))
935 {
936 GNUNET_break (0);
937 return GNUNET_SYSERR;
938 }
939 res = (uint16_t *) PQgetvalue (result,
940 row,
941 fnum);
942 *udst = ntohs (*res);
943 return GNUNET_OK;
944}
945
946
947struct GNUNET_PQ_ResultSpec
948GNUNET_PQ_result_spec_uint16 (const char *name,
949 uint16_t *u16)
950{
951 struct GNUNET_PQ_ResultSpec res = {
952 .conv = &extract_uint16,
953 .dst = (void *) u16,
954 .dst_size = sizeof(*u16),
955 .fname = name
956 };
957
958 return res;
959}
960
961
962/**
963 * Extract data from a Postgres database @a result at row @a row.
964 *
965 * @param cls closure
966 * @param result where to extract data from
967 * @param int row to extract data from
968 * @param fname name (or prefix) of the fields to extract from
969 * @param[in,out] dst_size where to store size of result, may be NULL
970 * @param[out] dst where to store the result
971 * @return
972 * #GNUNET_YES if all results could be extracted
973 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
974 */
975static enum GNUNET_GenericReturnValue
976extract_uint32 (void *cls,
977 PGresult *result,
978 int row,
979 const char *fname,
980 size_t *dst_size,
981 void *dst)
982{
983 uint32_t *udst = dst;
984 const uint32_t *res;
985 int fnum;
986
987 (void) cls;
988 fnum = PQfnumber (result,
989 fname);
990 if (fnum < 0)
991 {
992 GNUNET_break (0);
993 return GNUNET_SYSERR;
994 }
995 if (PQgetisnull (result,
996 row,
997 fnum))
998 return GNUNET_NO;
999 GNUNET_assert (NULL != dst);
1000 if (sizeof(uint32_t) != *dst_size)
1001 {
1002 GNUNET_break (0);
1003 return GNUNET_SYSERR;
1004 }
1005 if (sizeof(uint32_t) !=
1006 PQgetlength (result,
1007 row,
1008 fnum))
1009 {
1010 GNUNET_break (0);
1011 return GNUNET_SYSERR;
1012 }
1013 res = (uint32_t *) PQgetvalue (result,
1014 row,
1015 fnum);
1016 *udst = ntohl (*res);
1017 return GNUNET_OK;
1018}
1019
1020
1021struct GNUNET_PQ_ResultSpec
1022GNUNET_PQ_result_spec_uint32 (const char *name,
1023 uint32_t *u32)
1024{
1025 struct GNUNET_PQ_ResultSpec res = {
1026 .conv = &extract_uint32,
1027 .dst = (void *) u32,
1028 .dst_size = sizeof(*u32),
1029 .fname = name
1030 };
1031
1032 return res;
1033}
1034
1035
1036/**
1037 * Extract data from a Postgres database @a result at row @a row.
1038 *
1039 * @param cls closure
1040 * @param result where to extract data from
1041 * @param int row to extract data from
1042 * @param fname name (or prefix) of the fields to extract from
1043 * @param[in,out] dst_size where to store size of result, may be NULL
1044 * @param[out] dst where to store the result
1045 * @return
1046 * #GNUNET_YES if all results could be extracted
1047 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
1048 */
1049static enum GNUNET_GenericReturnValue
1050extract_uint64 (void *cls,
1051 PGresult *result,
1052 int row,
1053 const char *fname,
1054 size_t *dst_size,
1055 void *dst)
1056{
1057 uint64_t *udst = dst;
1058 const uint64_t *res;
1059 int fnum;
1060
1061 (void) cls;
1062 fnum = PQfnumber (result,
1063 fname);
1064 if (fnum < 0)
1065 {
1066 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1067 "Field %s missing in result\n",
1068 fname);
1069 GNUNET_break (0);
1070 return GNUNET_SYSERR;
1071 }
1072 if (PQgetisnull (result,
1073 row,
1074 fnum))
1075 return GNUNET_NO;
1076
1077 GNUNET_assert (NULL != dst);
1078 if (sizeof(uint64_t) != *dst_size)
1079 {
1080 GNUNET_break (0);
1081 return GNUNET_SYSERR;
1082 }
1083 if (sizeof(uint64_t) !=
1084 PQgetlength (result,
1085 row,
1086 fnum))
1087 {
1088 GNUNET_break (0);
1089 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1090 "Got length %u for field `%s'\n",
1091 PQgetlength (result,
1092 row,
1093 fnum),
1094 fname);
1095 return GNUNET_SYSERR;
1096 }
1097 res = (uint64_t *) PQgetvalue (result,
1098 row,
1099 fnum);
1100 *udst = GNUNET_ntohll (*res);
1101 return GNUNET_OK;
1102}
1103
1104
1105struct GNUNET_PQ_ResultSpec
1106GNUNET_PQ_result_spec_uint64 (const char *name,
1107 uint64_t *u64)
1108{
1109 struct GNUNET_PQ_ResultSpec res = {
1110 .conv = &extract_uint64,
1111 .dst = (void *) u64,
1112 .dst_size = sizeof(*u64),
1113 .fname = name
1114 };
1115
1116 return res;
1117}
1118
1119
1120/* 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 */