diff options
Diffstat (limited to 'src/pq')
-rw-r--r-- | src/pq/.gitignore | 1 | ||||
-rw-r--r-- | src/pq/Makefile.am | 42 | ||||
-rw-r--r-- | src/pq/pq.c | 190 | ||||
-rw-r--r-- | src/pq/pq.h | 101 | ||||
-rw-r--r-- | src/pq/pq_connect.c | 543 | ||||
-rw-r--r-- | src/pq/pq_eval.c | 307 | ||||
-rw-r--r-- | src/pq/pq_event.c | 563 | ||||
-rw-r--r-- | src/pq/pq_exec.c | 117 | ||||
-rw-r--r-- | src/pq/pq_prepare.c | 132 | ||||
-rw-r--r-- | src/pq/pq_query_helper.c | 572 | ||||
-rw-r--r-- | src/pq/pq_result_helper.c | 1120 | ||||
-rw-r--r-- | src/pq/test_pq.c | 421 |
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 @@ | |||
1 | test_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 | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS) | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage | ||
6 | endif | ||
7 | |||
8 | if HAVE_POSTGRESQL | ||
9 | lib_LTLIBRARIES = libgnunetpq.la | ||
10 | endif | ||
11 | |||
12 | libgnunetpq_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 | ||
22 | libgnunetpq_la_LIBADD = -lpq \ | ||
23 | $(top_builddir)/src/util/libgnunetutil.la | ||
24 | libgnunetpq_la_LDFLAGS = \ | ||
25 | $(POSTGRESQL_LDFLAGS) \ | ||
26 | $(GN_LIB_LDFLAGS) \ | ||
27 | -version-info 2:0:0 | ||
28 | |||
29 | if ENABLE_TEST_RUN | ||
30 | TESTS = \ | ||
31 | test_pq | ||
32 | endif | ||
33 | |||
34 | check_PROGRAMS= \ | ||
35 | test_pq | ||
36 | |||
37 | test_pq_SOURCES = \ | ||
38 | test_pq.c | ||
39 | test_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 | |||
31 | PGresult * | ||
32 | GNUNET_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 = ¶ms[i]; | ||
67 | |||
68 | ret = x->conv (x->conv_cls, | ||
69 | x->data, | ||
70 | x->size, | ||
71 | ¶m_values[off], | ||
72 | ¶m_lengths[off], | ||
73 | ¶m_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 | |||
121 | void | ||
122 | GNUNET_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 | |||
131 | enum GNUNET_GenericReturnValue | ||
132 | GNUNET_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; | ||
181 | cleanup: | ||
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 | */ | ||
35 | struct 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 | */ | ||
96 | void | ||
97 | GNUNET_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 | */ | ||
38 | static void | ||
39 | pq_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 | */ | ||
55 | static void | ||
56 | pq_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 | |||
67 | struct GNUNET_PQ_Context * | ||
68 | GNUNET_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 | |||
84 | struct GNUNET_PQ_Context * | ||
85 | GNUNET_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 | |||
142 | enum GNUNET_GenericReturnValue | ||
143 | GNUNET_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 | |||
222 | enum GNUNET_GenericReturnValue | ||
223 | GNUNET_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 | |||
312 | void | ||
313 | GNUNET_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 | |||
324 | void | ||
325 | GNUNET_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 | |||
460 | struct GNUNET_PQ_Context * | ||
461 | GNUNET_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 | |||
476 | struct GNUNET_PQ_Context * | ||
477 | GNUNET_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 | |||
525 | void | ||
526 | GNUNET_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 | */ | ||
58 | enum GNUNET_DB_QueryStatus | ||
59 | GNUNET_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 | */ | ||
163 | enum GNUNET_DB_QueryStatus | ||
164 | GNUNET_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 | */ | ||
208 | enum GNUNET_DB_QueryStatus | ||
209 | GNUNET_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 | */ | ||
257 | enum GNUNET_DB_QueryStatus | ||
258 | GNUNET_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 | */ | ||
33 | struct 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 | */ | ||
68 | static void | ||
69 | es_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 | */ | ||
92 | static char * | ||
93 | sh_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 | */ | ||
115 | static enum GNUNET_GenericReturnValue | ||
116 | channel_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 | */ | ||
134 | static char * | ||
135 | es_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 | */ | ||
150 | struct 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 | */ | ||
173 | static int | ||
174 | do_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 | |||
188 | static void | ||
189 | event_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 | */ | ||
271 | static void | ||
272 | do_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 | */ | ||
306 | static void | ||
307 | scheduler_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 | */ | ||
344 | static void | ||
345 | manage_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 | */ | ||
390 | static int | ||
391 | register_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 | |||
405 | void | ||
406 | GNUNET_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 | ®ister_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 | */ | ||
426 | static void | ||
427 | event_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 | |||
438 | struct GNUNET_DB_EventHandler * | ||
439 | GNUNET_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 | |||
480 | void | ||
481 | GNUNET_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 | |||
515 | void | ||
516 | GNUNET_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 | */ | ||
35 | struct GNUNET_PQ_ExecuteStatement | ||
36 | GNUNET_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 | */ | ||
54 | struct GNUNET_PQ_ExecuteStatement | ||
55 | GNUNET_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 | */ | ||
75 | enum GNUNET_GenericReturnValue | ||
76 | GNUNET_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 | */ | ||
37 | struct GNUNET_PQ_PreparedStatement | ||
38 | GNUNET_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 | */ | ||
61 | enum GNUNET_GenericReturnValue | ||
62 | GNUNET_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 | */ | ||
44 | static int | ||
45 | qconv_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 | |||
69 | struct GNUNET_PQ_QueryParam | ||
70 | GNUNET_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 | */ | ||
95 | static int | ||
96 | qconv_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 | |||
118 | struct GNUNET_PQ_QueryParam | ||
119 | GNUNET_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 | |||
130 | struct GNUNET_PQ_QueryParam | ||
131 | GNUNET_PQ_query_param_string (const char *ptr) | ||
132 | { | ||
133 | return GNUNET_PQ_query_param_fixed_size (ptr, | ||
134 | strlen (ptr)); | ||
135 | } | ||
136 | |||
137 | |||
138 | struct GNUNET_PQ_QueryParam | ||
139 | GNUNET_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 | */ | ||
163 | static int | ||
164 | qconv_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 | |||
192 | struct GNUNET_PQ_QueryParam | ||
193 | GNUNET_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 | */ | ||
220 | static int | ||
221 | qconv_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 | |||
249 | struct GNUNET_PQ_QueryParam | ||
250 | GNUNET_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 | */ | ||
277 | static int | ||
278 | qconv_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 | |||
306 | struct GNUNET_PQ_QueryParam | ||
307 | GNUNET_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 | */ | ||
334 | static int | ||
335 | qconv_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 | |||
362 | struct GNUNET_PQ_QueryParam | ||
363 | GNUNET_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 | */ | ||
390 | static int | ||
391 | qconv_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 | |||
418 | struct GNUNET_PQ_QueryParam | ||
419 | GNUNET_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 | */ | ||
445 | static int | ||
446 | qconv_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 | |||
476 | struct GNUNET_PQ_QueryParam | ||
477 | GNUNET_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 | */ | ||
504 | static int | ||
505 | qconv_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 | |||
535 | struct GNUNET_PQ_QueryParam | ||
536 | GNUNET_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 | |||
549 | struct GNUNET_PQ_QueryParam | ||
550 | GNUNET_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 | |||
557 | struct GNUNET_PQ_QueryParam | ||
558 | GNUNET_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 | |||
564 | struct GNUNET_PQ_QueryParam | ||
565 | GNUNET_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 | |||
30 | struct GNUNET_PQ_ResultSpec | ||
31 | GNUNET_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 | */ | ||
50 | static void | ||
51 | clean_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 | */ | ||
78 | static enum GNUNET_GenericReturnValue | ||
79 | extract_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 | |||
125 | struct GNUNET_PQ_ResultSpec | ||
126 | GNUNET_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 | */ | ||
155 | static enum GNUNET_GenericReturnValue | ||
156 | extract_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 | |||
208 | struct GNUNET_PQ_ResultSpec | ||
209 | GNUNET_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 | */ | ||
237 | static enum GNUNET_GenericReturnValue | ||
238 | extract_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 | */ | ||
290 | static void | ||
291 | clean_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 | |||
305 | struct GNUNET_PQ_ResultSpec | ||
306 | GNUNET_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 | */ | ||
333 | static enum GNUNET_GenericReturnValue | ||
334 | extract_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 | */ | ||
385 | static void | ||
386 | clean_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 | |||
400 | struct GNUNET_PQ_ResultSpec | ||
401 | GNUNET_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 | */ | ||
428 | static enum GNUNET_GenericReturnValue | ||
429 | extract_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 | */ | ||
480 | static void | ||
481 | clean_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 | |||
495 | struct GNUNET_PQ_ResultSpec | ||
496 | GNUNET_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 | */ | ||
523 | static enum GNUNET_GenericReturnValue | ||
524 | extract_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 | |||
566 | struct GNUNET_PQ_ResultSpec | ||
567 | GNUNET_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 | */ | ||
593 | static enum GNUNET_GenericReturnValue | ||
594 | extract_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 | |||
642 | struct GNUNET_PQ_ResultSpec | ||
643 | GNUNET_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 | */ | ||
670 | static enum GNUNET_GenericReturnValue | ||
671 | extract_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 | |||
719 | struct GNUNET_PQ_ResultSpec | ||
720 | GNUNET_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 | |||
734 | struct GNUNET_PQ_ResultSpec | ||
735 | GNUNET_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 | */ | ||
759 | static enum GNUNET_GenericReturnValue | ||
760 | extract_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 | |||
820 | struct GNUNET_PQ_ResultSpec | ||
821 | GNUNET_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 | */ | ||
848 | static enum GNUNET_GenericReturnValue | ||
849 | extract_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 | |||
873 | struct GNUNET_PQ_ResultSpec | ||
874 | GNUNET_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 | */ | ||
901 | static enum GNUNET_GenericReturnValue | ||
902 | extract_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 | |||
947 | struct GNUNET_PQ_ResultSpec | ||
948 | GNUNET_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 | */ | ||
975 | static enum GNUNET_GenericReturnValue | ||
976 | extract_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 | |||
1021 | struct GNUNET_PQ_ResultSpec | ||
1022 | GNUNET_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 | */ | ||
1049 | static enum GNUNET_GenericReturnValue | ||
1050 | extract_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 | |||
1105 | struct GNUNET_PQ_ResultSpec | ||
1106 | GNUNET_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 | */ | ||
31 | static struct GNUNET_PQ_Context *db; | ||
32 | |||
33 | /** | ||
34 | * Global return value, 0 on success. | ||
35 | */ | ||
36 | static int ret; | ||
37 | |||
38 | /** | ||
39 | * An event handler. | ||
40 | */ | ||
41 | static struct GNUNET_DB_EventHandler *eh; | ||
42 | |||
43 | /** | ||
44 | * Timeout task. | ||
45 | */ | ||
46 | static 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 | */ | ||
55 | static int | ||
56 | postgres_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 | */ | ||
105 | static int | ||
106 | run_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 | */ | ||
248 | static void | ||
249 | event_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 | */ | ||
267 | static void | ||
268 | timeout_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 | */ | ||
282 | static void | ||
283 | event_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 | */ | ||
301 | static void | ||
302 | sched_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 | |||
328 | int | ||
329 | main (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 */ | ||