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 | 96 | ||||
-rw-r--r-- | src/pq/pq_connect.c | 524 | ||||
-rw-r--r-- | src/pq/pq_eval.c | 307 | ||||
-rw-r--r-- | src/pq/pq_event.c | 558 | ||||
-rw-r--r-- | src/pq/pq_exec.c | 117 | ||||
-rw-r--r-- | src/pq/pq_prepare.c | 122 | ||||
-rw-r--r-- | src/pq/pq_query_helper.c | 520 | ||||
-rw-r--r-- | src/pq/pq_result_helper.c | 907 | ||||
-rw-r--r-- | src/pq/test_pq.c | 421 |
12 files changed, 0 insertions, 3805 deletions
diff --git a/src/pq/.gitignore b/src/pq/.gitignore deleted file mode 100644 index 8de68ddc9..000000000 --- a/src/pq/.gitignore +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | test_pq | ||
diff --git a/src/pq/Makefile.am b/src/pq/Makefile.am deleted file mode 100644 index cbb123cbb..000000000 --- a/src/pq/Makefile.am +++ /dev/null | |||
@@ -1,42 +0,0 @@ | |||
1 | # This Makefile.am is in the public domain | ||
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 1: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 354d85a9f..000000000 --- a/src/pq/pq.h +++ /dev/null | |||
@@ -1,96 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2017, 2019 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file pq/pq.h | ||
22 | * @brief shared internal data structures of libgnunetpq | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #ifndef PQ_H | ||
26 | #define PQ_H | ||
27 | |||
28 | #include "gnunet_util_lib.h" | ||
29 | #include "gnunet_pq_lib.h" | ||
30 | |||
31 | |||
32 | /** | ||
33 | * Handle to Postgres database. | ||
34 | */ | ||
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 | * Map managing event subscriptions. | ||
64 | */ | ||
65 | struct GNUNET_CONTAINER_MultiShortmap *channel_map; | ||
66 | |||
67 | /** | ||
68 | * Task responsible for processing events. | ||
69 | */ | ||
70 | struct GNUNET_SCHEDULER_Task *event_task; | ||
71 | |||
72 | /** | ||
73 | * File descriptor wrapper for @e event_task. | ||
74 | */ | ||
75 | struct GNUNET_NETWORK_Handle *rfd; | ||
76 | |||
77 | /** | ||
78 | * Flags controlling the connection. | ||
79 | */ | ||
80 | enum GNUNET_PQ_Options flags; | ||
81 | }; | ||
82 | |||
83 | |||
84 | /** | ||
85 | * Internal API. Reconnect should re-register notifications | ||
86 | * after a disconnect. | ||
87 | * | ||
88 | * @param db the DB handle | ||
89 | * @param fd socket to listen on | ||
90 | */ | ||
91 | void | ||
92 | GNUNET_PQ_event_reconnect_ (struct GNUNET_PQ_Context *db, | ||
93 | int fd); | ||
94 | |||
95 | |||
96 | #endif | ||
diff --git a/src/pq/pq_connect.c b/src/pq/pq_connect.c deleted file mode 100644 index a63d5f14e..000000000 --- a/src/pq/pq_connect.c +++ /dev/null | |||
@@ -1,524 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2017, 2019, 2020, 2021 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file pq/pq_connect.c | ||
22 | * @brief functions to connect to libpq (PostGres) | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "pq.h" | ||
27 | #include <pthread.h> | ||
28 | |||
29 | |||
30 | /** | ||
31 | * Function called by libpq whenever it wants to log something. | ||
32 | * We already log whenever we care, so this function does nothing | ||
33 | * and merely exists to silence the libpq logging. | ||
34 | * | ||
35 | * @param arg the SQL connection that was used | ||
36 | * @param res information about some libpq event | ||
37 | */ | ||
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 | es, | ||
76 | ps, | ||
77 | GNUNET_PQ_FLAG_NONE); | ||
78 | } | ||
79 | |||
80 | |||
81 | struct GNUNET_PQ_Context * | ||
82 | GNUNET_PQ_connect2 (const char *config_str, | ||
83 | const char *load_path, | ||
84 | const struct GNUNET_PQ_ExecuteStatement *es, | ||
85 | const struct GNUNET_PQ_PreparedStatement *ps, | ||
86 | enum GNUNET_PQ_Options flags) | ||
87 | { | ||
88 | struct GNUNET_PQ_Context *db; | ||
89 | unsigned int elen = 0; | ||
90 | unsigned int plen = 0; | ||
91 | |||
92 | if (NULL != es) | ||
93 | while (NULL != es[elen].sql) | ||
94 | elen++; | ||
95 | if (NULL != ps) | ||
96 | while (NULL != ps[plen].name) | ||
97 | plen++; | ||
98 | |||
99 | db = GNUNET_new (struct GNUNET_PQ_Context); | ||
100 | db->flags = flags; | ||
101 | db->config_str = GNUNET_strdup (config_str); | ||
102 | if (NULL != load_path) | ||
103 | db->load_path = GNUNET_strdup (load_path); | ||
104 | if (0 != elen) | ||
105 | { | ||
106 | db->es = GNUNET_new_array (elen + 1, | ||
107 | struct GNUNET_PQ_ExecuteStatement); | ||
108 | memcpy (db->es, | ||
109 | es, | ||
110 | elen * sizeof (struct GNUNET_PQ_ExecuteStatement)); | ||
111 | } | ||
112 | if (0 != plen) | ||
113 | { | ||
114 | db->ps = GNUNET_new_array (plen + 1, | ||
115 | struct GNUNET_PQ_PreparedStatement); | ||
116 | memcpy (db->ps, | ||
117 | ps, | ||
118 | plen * sizeof (struct GNUNET_PQ_PreparedStatement)); | ||
119 | } | ||
120 | db->channel_map = GNUNET_CONTAINER_multishortmap_create (16, | ||
121 | GNUNET_YES); | ||
122 | GNUNET_PQ_reconnect (db); | ||
123 | if (NULL == db->conn) | ||
124 | { | ||
125 | GNUNET_free (db->load_path); | ||
126 | GNUNET_free (db->config_str); | ||
127 | GNUNET_free (db); | ||
128 | return NULL; | ||
129 | } | ||
130 | return db; | ||
131 | } | ||
132 | |||
133 | |||
134 | /** | ||
135 | * Apply patch number @a from path @a load_path. | ||
136 | * | ||
137 | * @param db database context to use | ||
138 | * @param load_path where to find the SQL code to run | ||
139 | * @param i patch number to append to the @a load_path | ||
140 | * @return #GNUNET_OK on success, #GNUNET_NO if patch @a i does not exist, #GNUNET_SYSERR on error | ||
141 | */ | ||
142 | static enum GNUNET_GenericReturnValue | ||
143 | apply_patch (struct GNUNET_PQ_Context *db, | ||
144 | const char *load_path, | ||
145 | unsigned int i) | ||
146 | { | ||
147 | struct GNUNET_OS_Process *psql; | ||
148 | enum GNUNET_OS_ProcessStatusType type; | ||
149 | unsigned long code; | ||
150 | size_t slen = strlen (load_path) + 10; | ||
151 | char buf[slen]; | ||
152 | |||
153 | GNUNET_snprintf (buf, | ||
154 | sizeof (buf), | ||
155 | "%s%04u.sql", | ||
156 | load_path, | ||
157 | i); | ||
158 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
159 | "Applying SQL file `%s' on database %s\n", | ||
160 | buf, | ||
161 | db->config_str); | ||
162 | psql = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR, | ||
163 | NULL, | ||
164 | NULL, | ||
165 | NULL, | ||
166 | "psql", | ||
167 | "psql", | ||
168 | db->config_str, | ||
169 | "-f", | ||
170 | buf, | ||
171 | "-q", | ||
172 | "--set", | ||
173 | "ON_ERROR_STOP=1", | ||
174 | NULL); | ||
175 | if (NULL == psql) | ||
176 | { | ||
177 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
178 | "exec", | ||
179 | "psql"); | ||
180 | return GNUNET_SYSERR; | ||
181 | } | ||
182 | GNUNET_assert (GNUNET_OK == | ||
183 | GNUNET_OS_process_wait_status (psql, | ||
184 | &type, | ||
185 | &code)); | ||
186 | GNUNET_OS_process_destroy (psql); | ||
187 | if ( (GNUNET_OS_PROCESS_EXITED != type) || | ||
188 | (0 != code) ) | ||
189 | { | ||
190 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
191 | "Could not run PSQL on file %s: psql exit code was %d\n", | ||
192 | buf, | ||
193 | (int) code); | ||
194 | return GNUNET_SYSERR; | ||
195 | } | ||
196 | return GNUNET_OK; | ||
197 | } | ||
198 | |||
199 | |||
200 | enum GNUNET_GenericReturnValue | ||
201 | GNUNET_PQ_run_sql (struct GNUNET_PQ_Context *db, | ||
202 | const char *load_path) | ||
203 | { | ||
204 | const char *load_path_suffix; | ||
205 | size_t slen = strlen (load_path) + 10; | ||
206 | |||
207 | load_path_suffix = strrchr (load_path, '/'); | ||
208 | if (NULL == load_path_suffix) | ||
209 | { | ||
210 | GNUNET_break (0); | ||
211 | return GNUNET_SYSERR; | ||
212 | } | ||
213 | load_path_suffix++; /* skip '/' */ | ||
214 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
215 | "Loading SQL resources from `%s'\n", | ||
216 | load_path); | ||
217 | for (unsigned int i = 1; i<10000; i++) | ||
218 | { | ||
219 | char patch_name[slen]; | ||
220 | char buf[slen]; | ||
221 | enum GNUNET_DB_QueryStatus qs; | ||
222 | |||
223 | /* First, check patch actually exists */ | ||
224 | GNUNET_snprintf (buf, | ||
225 | sizeof (buf), | ||
226 | "%s%04u.sql", | ||
227 | load_path, | ||
228 | i); | ||
229 | if (GNUNET_YES != | ||
230 | GNUNET_DISK_file_test (buf)) | ||
231 | return GNUNET_OK; /* We are done */ | ||
232 | |||
233 | /* Second, check with DB versioning schema if this patch was already applied, | ||
234 | if so, skip it. */ | ||
235 | GNUNET_snprintf (patch_name, | ||
236 | sizeof (patch_name), | ||
237 | "%s%04u", | ||
238 | load_path_suffix, | ||
239 | i); | ||
240 | { | ||
241 | char *applied_by; | ||
242 | struct GNUNET_PQ_QueryParam params[] = { | ||
243 | GNUNET_PQ_query_param_string (patch_name), | ||
244 | GNUNET_PQ_query_param_end | ||
245 | }; | ||
246 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
247 | GNUNET_PQ_result_spec_string ("applied_by", | ||
248 | &applied_by), | ||
249 | GNUNET_PQ_result_spec_end | ||
250 | }; | ||
251 | |||
252 | qs = GNUNET_PQ_eval_prepared_singleton_select (db, | ||
253 | "gnunet_pq_check_patch", | ||
254 | params, | ||
255 | rs); | ||
256 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) | ||
257 | { | ||
258 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
259 | "Database version %s already applied by %s, skipping\n", | ||
260 | patch_name, | ||
261 | applied_by); | ||
262 | GNUNET_PQ_cleanup_result (rs); | ||
263 | } | ||
264 | if (GNUNET_DB_STATUS_HARD_ERROR == qs) | ||
265 | { | ||
266 | GNUNET_break (0); | ||
267 | return GNUNET_SYSERR; | ||
268 | } | ||
269 | } | ||
270 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) | ||
271 | continue; /* patch already applied, skip it */ | ||
272 | |||
273 | if (0 != (GNUNET_PQ_FLAG_CHECK_CURRENT & db->flags)) | ||
274 | { | ||
275 | /* We are only checking, found unapplied patch, bad! */ | ||
276 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
277 | "Database outdated, patch %s missing. Aborting!\n", | ||
278 | patch_name); | ||
279 | return GNUNET_SYSERR; | ||
280 | } | ||
281 | else | ||
282 | { | ||
283 | /* patch not yet applied, run it! */ | ||
284 | enum GNUNET_GenericReturnValue ret; | ||
285 | |||
286 | ret = apply_patch (db, | ||
287 | load_path, | ||
288 | i); | ||
289 | if (GNUNET_NO == ret) | ||
290 | break; | ||
291 | if (GNUNET_SYSERR == ret) | ||
292 | return GNUNET_SYSERR; | ||
293 | } | ||
294 | } | ||
295 | return GNUNET_OK; | ||
296 | } | ||
297 | |||
298 | |||
299 | void | ||
300 | GNUNET_PQ_reconnect_if_down (struct GNUNET_PQ_Context *db) | ||
301 | { | ||
302 | if (1 == | ||
303 | PQconsumeInput (db->conn)) | ||
304 | return; | ||
305 | if (CONNECTION_BAD != PQstatus (db->conn)) | ||
306 | return; | ||
307 | GNUNET_PQ_reconnect (db); | ||
308 | } | ||
309 | |||
310 | |||
311 | void | ||
312 | GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db) | ||
313 | { | ||
314 | GNUNET_PQ_event_reconnect_ (db, | ||
315 | -1); | ||
316 | if (NULL != db->conn) | ||
317 | PQfinish (db->conn); | ||
318 | db->conn = PQconnectdb (db->config_str); | ||
319 | if ( (NULL == db->conn) || | ||
320 | (CONNECTION_OK != PQstatus (db->conn)) ) | ||
321 | { | ||
322 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, | ||
323 | "pq", | ||
324 | "Database connection to '%s' failed: %s\n", | ||
325 | db->config_str, | ||
326 | (NULL != db->conn) ? | ||
327 | PQerrorMessage (db->conn) | ||
328 | : "PQconnectdb returned NULL"); | ||
329 | if (NULL != db->conn) | ||
330 | { | ||
331 | PQfinish (db->conn); | ||
332 | db->conn = NULL; | ||
333 | } | ||
334 | return; | ||
335 | } | ||
336 | PQsetNoticeReceiver (db->conn, | ||
337 | &pq_notice_receiver_cb, | ||
338 | db); | ||
339 | PQsetNoticeProcessor (db->conn, | ||
340 | &pq_notice_processor_cb, | ||
341 | db); | ||
342 | if (NULL != db->load_path) | ||
343 | { | ||
344 | PGresult *res; | ||
345 | |||
346 | res = PQprepare (db->conn, | ||
347 | "gnunet_pq_check_patch", | ||
348 | "SELECT" | ||
349 | " applied_by" | ||
350 | " FROM _v.patches" | ||
351 | " WHERE patch_name = $1" | ||
352 | " LIMIT 1", | ||
353 | 1, | ||
354 | NULL); | ||
355 | if (PGRES_COMMAND_OK != PQresultStatus (res)) | ||
356 | { | ||
357 | enum GNUNET_GenericReturnValue ret; | ||
358 | |||
359 | PQclear (res); | ||
360 | if (0 != (db->flags & GNUNET_PQ_FLAG_DROP)) | ||
361 | { | ||
362 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
363 | "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet. Not attempting drop!\n"); | ||
364 | PQfinish (db->conn); | ||
365 | db->conn = NULL; | ||
366 | return; | ||
367 | } | ||
368 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
369 | "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet, loading patch level 0000!\n"); | ||
370 | ret = apply_patch (db, | ||
371 | db->load_path, | ||
372 | 0); | ||
373 | if (GNUNET_NO == ret) | ||
374 | { | ||
375 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
376 | "Failed to find SQL file to load database versioning logic\n"); | ||
377 | PQfinish (db->conn); | ||
378 | db->conn = NULL; | ||
379 | return; | ||
380 | } | ||
381 | if (GNUNET_SYSERR == ret) | ||
382 | { | ||
383 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
384 | "Failed to run SQL logic to setup database versioning logic\n"); | ||
385 | PQfinish (db->conn); | ||
386 | db->conn = NULL; | ||
387 | return; | ||
388 | } | ||
389 | /* try again to prepare our statement! */ | ||
390 | res = PQprepare (db->conn, | ||
391 | "gnunet_pq_check_patch", | ||
392 | "SELECT" | ||
393 | " applied_by" | ||
394 | " FROM _v.patches" | ||
395 | " WHERE patch_name = $1" | ||
396 | " LIMIT 1", | ||
397 | 1, | ||
398 | NULL); | ||
399 | if (PGRES_COMMAND_OK != PQresultStatus (res)) | ||
400 | { | ||
401 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
402 | "Failed to run SQL logic to setup database versioning logic: %s/%s\n", | ||
403 | PQresultErrorMessage (res), | ||
404 | PQerrorMessage (db->conn)); | ||
405 | PQclear (res); | ||
406 | PQfinish (db->conn); | ||
407 | db->conn = NULL; | ||
408 | return; | ||
409 | } | ||
410 | } | ||
411 | PQclear (res); | ||
412 | |||
413 | if (GNUNET_SYSERR == | ||
414 | GNUNET_PQ_run_sql (db, | ||
415 | db->load_path)) | ||
416 | { | ||
417 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
418 | "Failed to load SQL statements from `%s*'\n", | ||
419 | db->load_path); | ||
420 | PQfinish (db->conn); | ||
421 | db->conn = NULL; | ||
422 | return; | ||
423 | } | ||
424 | } | ||
425 | if ( (NULL != db->es) && | ||
426 | (GNUNET_OK != | ||
427 | GNUNET_PQ_exec_statements (db, | ||
428 | db->es)) ) | ||
429 | { | ||
430 | PQfinish (db->conn); | ||
431 | db->conn = NULL; | ||
432 | return; | ||
433 | } | ||
434 | if ( (NULL != db->ps) && | ||
435 | (GNUNET_OK != | ||
436 | GNUNET_PQ_prepare_statements (db, | ||
437 | db->ps)) ) | ||
438 | { | ||
439 | PQfinish (db->conn); | ||
440 | db->conn = NULL; | ||
441 | return; | ||
442 | } | ||
443 | GNUNET_PQ_event_reconnect_ (db, | ||
444 | PQsocket (db->conn)); | ||
445 | } | ||
446 | |||
447 | |||
448 | struct GNUNET_PQ_Context * | ||
449 | GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
450 | const char *section, | ||
451 | const char *load_path_suffix, | ||
452 | const struct GNUNET_PQ_ExecuteStatement *es, | ||
453 | const struct GNUNET_PQ_PreparedStatement *ps) | ||
454 | { | ||
455 | return GNUNET_PQ_connect_with_cfg2 (cfg, | ||
456 | section, | ||
457 | load_path_suffix, | ||
458 | es, | ||
459 | ps, | ||
460 | GNUNET_PQ_FLAG_NONE); | ||
461 | } | ||
462 | |||
463 | |||
464 | struct GNUNET_PQ_Context * | ||
465 | GNUNET_PQ_connect_with_cfg2 (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
466 | const char *section, | ||
467 | const char *load_path_suffix, | ||
468 | const struct GNUNET_PQ_ExecuteStatement *es, | ||
469 | const struct GNUNET_PQ_PreparedStatement *ps, | ||
470 | enum GNUNET_PQ_Options flags) | ||
471 | { | ||
472 | struct GNUNET_PQ_Context *db; | ||
473 | char *conninfo; | ||
474 | char *load_path; | ||
475 | char *sp; | ||
476 | |||
477 | if (GNUNET_OK != | ||
478 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
479 | section, | ||
480 | "CONFIG", | ||
481 | &conninfo)) | ||
482 | conninfo = NULL; | ||
483 | load_path = NULL; | ||
484 | sp = NULL; | ||
485 | if ( (NULL != load_path_suffix) && | ||
486 | (GNUNET_OK == | ||
487 | GNUNET_CONFIGURATION_get_value_filename (cfg, | ||
488 | section, | ||
489 | "SQL_DIR", | ||
490 | &sp)) ) | ||
491 | GNUNET_asprintf (&load_path, | ||
492 | "%s%s", | ||
493 | sp, | ||
494 | load_path_suffix); | ||
495 | db = GNUNET_PQ_connect2 (conninfo == NULL ? "" : conninfo, | ||
496 | load_path, | ||
497 | es, | ||
498 | ps, | ||
499 | flags); | ||
500 | GNUNET_free (load_path); | ||
501 | GNUNET_free (sp); | ||
502 | GNUNET_free (conninfo); | ||
503 | return db; | ||
504 | } | ||
505 | |||
506 | |||
507 | void | ||
508 | GNUNET_PQ_disconnect (struct GNUNET_PQ_Context *db) | ||
509 | { | ||
510 | if (NULL == db) | ||
511 | return; | ||
512 | GNUNET_assert (0 == | ||
513 | GNUNET_CONTAINER_multishortmap_size (db->channel_map)); | ||
514 | GNUNET_CONTAINER_multishortmap_destroy (db->channel_map); | ||
515 | GNUNET_free (db->es); | ||
516 | GNUNET_free (db->ps); | ||
517 | GNUNET_free (db->load_path); | ||
518 | GNUNET_free (db->config_str); | ||
519 | PQfinish (db->conn); | ||
520 | GNUNET_free (db); | ||
521 | } | ||
522 | |||
523 | |||
524 | /* end of pq/pq_connect.c */ | ||
diff --git a/src/pq/pq_eval.c b/src/pq/pq_eval.c deleted file mode 100644 index 5f96ff884..000000000 --- a/src/pq/pq_eval.c +++ /dev/null | |||
@@ -1,307 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2017, 2019 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file pq/pq_eval.c | ||
22 | * @brief functions to execute SQL statements with arguments and/or results (PostGres) | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "pq.h" | ||
27 | |||
28 | |||
29 | /** | ||
30 | * Error code returned by Postgres for deadlock. | ||
31 | */ | ||
32 | #define PQ_DIAG_SQLSTATE_DEADLOCK "40P01" | ||
33 | |||
34 | /** | ||
35 | * Error code returned by Postgres for uniqueness violation. | ||
36 | */ | ||
37 | #define PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION "23505" | ||
38 | |||
39 | /** | ||
40 | * Error code returned by Postgres on serialization failure. | ||
41 | */ | ||
42 | #define PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE "40001" | ||
43 | |||
44 | |||
45 | /** | ||
46 | * Check the @a result's error code to see what happened. | ||
47 | * Also logs errors. | ||
48 | * | ||
49 | * @param db database to execute the statement with | ||
50 | * @param statement_name name of the statement that created @a result | ||
51 | * @param result result to check | ||
52 | * @return status code from the result, mapping PQ status | ||
53 | * codes to `enum GNUNET_DB_QueryStatus`. Never | ||
54 | * returns positive values as this function does | ||
55 | * not look at the result set. | ||
56 | * @deprecated (low level, let's see if we can do with just the high-level functions) | ||
57 | */ | ||
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 98a28c317..000000000 --- a/src/pq/pq_event.c +++ /dev/null | |||
@@ -1,558 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2021 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file pq/pq_event.c | ||
22 | * @brief event notifications via Postgres | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "pq.h" | ||
27 | #include <pthread.h> | ||
28 | |||
29 | |||
30 | /** | ||
31 | * Handle for an active LISTENer to the database. | ||
32 | */ | ||
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 | |||
193 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
194 | "PG poll job active\n"); | ||
195 | if (1 != | ||
196 | PQconsumeInput (db->conn)) | ||
197 | { | ||
198 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
199 | "Failed to read from Postgres: %s\n", | ||
200 | PQerrorMessage (db->conn)); | ||
201 | if (CONNECTION_BAD != PQstatus (db->conn)) | ||
202 | return; | ||
203 | GNUNET_PQ_reconnect (db); | ||
204 | return; | ||
205 | } | ||
206 | while (NULL != (n = PQnotifies (db->conn))) | ||
207 | { | ||
208 | struct GNUNET_ShortHashCode sh; | ||
209 | struct NotifyContext ctx = { | ||
210 | .extra = NULL | ||
211 | }; | ||
212 | |||
213 | if ('X' != toupper ((int) n->relname[0])) | ||
214 | { | ||
215 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
216 | "Ignoring notification for unsupported channel identifier `%s'\n", | ||
217 | n->relname); | ||
218 | PQfreemem (n); | ||
219 | continue; | ||
220 | } | ||
221 | if (GNUNET_OK != | ||
222 | channel_to_sh (&n->relname[1], | ||
223 | &sh)) | ||
224 | { | ||
225 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
226 | "Ignoring notification for unsupported channel identifier `%s'\n", | ||
227 | n->relname); | ||
228 | PQfreemem (n); | ||
229 | continue; | ||
230 | } | ||
231 | if ( (NULL != n->extra) && | ||
232 | (GNUNET_OK != | ||
233 | GNUNET_STRINGS_string_to_data_alloc (n->extra, | ||
234 | strlen (n->extra), | ||
235 | &ctx.extra, | ||
236 | &ctx.extra_size))) | ||
237 | { | ||
238 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
239 | "Ignoring notification for unsupported extra data `%s' on channel `%s'\n", | ||
240 | n->extra, | ||
241 | n->relname); | ||
242 | PQfreemem (n); | ||
243 | continue; | ||
244 | } | ||
245 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
246 | "Received notification %s with extra data `%.*s'\n", | ||
247 | n->relname, | ||
248 | (int) ctx.extra_size, | ||
249 | (const char *) ctx.extra); | ||
250 | GNUNET_CONTAINER_multishortmap_get_multiple (db->channel_map, | ||
251 | &sh, | ||
252 | &do_notify, | ||
253 | &ctx); | ||
254 | GNUNET_free (ctx.extra); | ||
255 | PQfreemem (n); | ||
256 | } | ||
257 | } | ||
258 | |||
259 | |||
260 | /** | ||
261 | * The GNUnet scheduler notifies us that we need to | ||
262 | * trigger the DB event poller. | ||
263 | * | ||
264 | * @param cls a `struct GNUNET_PQ_Context *` | ||
265 | */ | ||
266 | static void | ||
267 | do_scheduler_notify (void *cls) | ||
268 | { | ||
269 | struct GNUNET_PQ_Context *db = cls; | ||
270 | |||
271 | db->event_task = NULL; | ||
272 | if (NULL == db->rfd) | ||
273 | GNUNET_PQ_reconnect (db); | ||
274 | event_do_poll (db); | ||
275 | if (NULL != db->event_task) | ||
276 | return; | ||
277 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
278 | "Resubscribing\n"); | ||
279 | if (NULL == db->rfd) | ||
280 | { | ||
281 | db->event_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, | ||
282 | &do_scheduler_notify, | ||
283 | db); | ||
284 | return; | ||
285 | } | ||
286 | db->event_task | ||
287 | = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
288 | db->rfd, | ||
289 | &do_scheduler_notify, | ||
290 | db); | ||
291 | } | ||
292 | |||
293 | |||
294 | /** | ||
295 | * Function called when the Postgres FD changes and we need | ||
296 | * to update the scheduler event loop task. | ||
297 | * | ||
298 | * @param cls a `struct GNUNET_PQ_Context *` | ||
299 | * @param fd the file descriptor, possibly -1 | ||
300 | */ | ||
301 | static void | ||
302 | scheduler_fd_cb (void *cls, | ||
303 | int fd) | ||
304 | { | ||
305 | struct GNUNET_PQ_Context *db = cls; | ||
306 | |||
307 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
308 | "New poll FD is %d\n", | ||
309 | fd); | ||
310 | if (NULL != db->event_task) | ||
311 | { | ||
312 | GNUNET_SCHEDULER_cancel (db->event_task); | ||
313 | db->event_task = NULL; | ||
314 | } | ||
315 | GNUNET_free (db->rfd); | ||
316 | if (-1 == fd) | ||
317 | return; | ||
318 | if (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map)) | ||
319 | return; | ||
320 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
321 | "Activating poll job on %d\n", | ||
322 | fd); | ||
323 | db->rfd = GNUNET_NETWORK_socket_box_native (fd); | ||
324 | db->event_task | ||
325 | = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_ZERO, | ||
326 | db->rfd, | ||
327 | &do_scheduler_notify, | ||
328 | db); | ||
329 | } | ||
330 | |||
331 | |||
332 | /** | ||
333 | * Helper function to trigger an SQL @a cmd on @a db | ||
334 | * | ||
335 | * @param db database to send command to | ||
336 | * @param cmd prefix of the command to send | ||
337 | * @param eh details about the event | ||
338 | */ | ||
339 | static void | ||
340 | manage_subscribe (struct GNUNET_PQ_Context *db, | ||
341 | const char *cmd, | ||
342 | struct GNUNET_DB_EventHandler *eh) | ||
343 | { | ||
344 | char sql[16 + 64]; | ||
345 | char *end; | ||
346 | PGresult *result; | ||
347 | |||
348 | if (NULL == db->conn) | ||
349 | return; | ||
350 | end = stpcpy (sql, | ||
351 | cmd); | ||
352 | end = sh_to_channel (&eh->sh, | ||
353 | end); | ||
354 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
355 | "Executing PQ command `%s'\n", | ||
356 | sql); | ||
357 | result = PQexec (db->conn, | ||
358 | sql); | ||
359 | if (PGRES_COMMAND_OK != PQresultStatus (result)) | ||
360 | { | ||
361 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, | ||
362 | "pq", | ||
363 | "Failed to execute `%s': %s/%s/%s/%s/%s", | ||
364 | sql, | ||
365 | PQresultErrorField (result, | ||
366 | PG_DIAG_MESSAGE_PRIMARY), | ||
367 | PQresultErrorField (result, | ||
368 | PG_DIAG_MESSAGE_DETAIL), | ||
369 | PQresultErrorMessage (result), | ||
370 | PQresStatus (PQresultStatus (result)), | ||
371 | PQerrorMessage (db->conn)); | ||
372 | } | ||
373 | PQclear (result); | ||
374 | } | ||
375 | |||
376 | |||
377 | /** | ||
378 | * Re-subscribe to notifications after disconnect. | ||
379 | * | ||
380 | * @param cls the DB context | ||
381 | * @param sh the short hash of the channel | ||
382 | * @param eh the event handler | ||
383 | * @return #GNUNET_OK to continue to iterate | ||
384 | */ | ||
385 | static int | ||
386 | register_notify (void *cls, | ||
387 | const struct GNUNET_ShortHashCode *sh, | ||
388 | void *value) | ||
389 | { | ||
390 | struct GNUNET_PQ_Context *db = cls; | ||
391 | struct GNUNET_DB_EventHandler *eh = value; | ||
392 | |||
393 | manage_subscribe (db, | ||
394 | "LISTEN X", | ||
395 | eh); | ||
396 | return GNUNET_OK; | ||
397 | } | ||
398 | |||
399 | |||
400 | void | ||
401 | GNUNET_PQ_event_reconnect_ (struct GNUNET_PQ_Context *db, | ||
402 | int fd) | ||
403 | { | ||
404 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
405 | "Change in PQ event FD to %d\n", | ||
406 | fd); | ||
407 | scheduler_fd_cb (db, | ||
408 | fd); | ||
409 | GNUNET_CONTAINER_multishortmap_iterate (db->channel_map, | ||
410 | ®ister_notify, | ||
411 | db); | ||
412 | } | ||
413 | |||
414 | |||
415 | /** | ||
416 | * Function run on timeout for an event. Triggers | ||
417 | * the notification, but does NOT clear the handler. | ||
418 | * | ||
419 | * @param cls a `struct GNUNET_DB_EventHandler *` | ||
420 | */ | ||
421 | static void | ||
422 | event_timeout (void *cls) | ||
423 | { | ||
424 | struct GNUNET_DB_EventHandler *eh = cls; | ||
425 | |||
426 | eh->timeout_task = NULL; | ||
427 | eh->cb (eh->cb_cls, | ||
428 | NULL, | ||
429 | 0); | ||
430 | } | ||
431 | |||
432 | |||
433 | struct GNUNET_DB_EventHandler * | ||
434 | GNUNET_PQ_event_listen (struct GNUNET_PQ_Context *db, | ||
435 | const struct GNUNET_DB_EventHeaderP *es, | ||
436 | struct GNUNET_TIME_Relative timeout, | ||
437 | GNUNET_DB_EventCallback cb, | ||
438 | void *cb_cls) | ||
439 | { | ||
440 | struct GNUNET_DB_EventHandler *eh; | ||
441 | bool sub; | ||
442 | |||
443 | eh = GNUNET_new (struct GNUNET_DB_EventHandler); | ||
444 | eh->db = db; | ||
445 | es_to_sh (es, | ||
446 | &eh->sh); | ||
447 | eh->cb = cb; | ||
448 | eh->cb_cls = cb_cls; | ||
449 | sub = (NULL == | ||
450 | GNUNET_CONTAINER_multishortmap_get (db->channel_map, | ||
451 | &eh->sh)); | ||
452 | GNUNET_assert (GNUNET_OK == | ||
453 | GNUNET_CONTAINER_multishortmap_put (db->channel_map, | ||
454 | &eh->sh, | ||
455 | eh, | ||
456 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); | ||
457 | if (NULL == db->event_task) | ||
458 | { | ||
459 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
460 | "Starting event scheduler\n"); | ||
461 | scheduler_fd_cb (db, | ||
462 | PQsocket (db->conn)); | ||
463 | } | ||
464 | if (sub) | ||
465 | manage_subscribe (db, | ||
466 | "LISTEN X", | ||
467 | eh); | ||
468 | eh->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, | ||
469 | &event_timeout, | ||
470 | eh); | ||
471 | return eh; | ||
472 | } | ||
473 | |||
474 | |||
475 | void | ||
476 | GNUNET_PQ_event_listen_cancel (struct GNUNET_DB_EventHandler *eh) | ||
477 | { | ||
478 | struct GNUNET_PQ_Context *db = eh->db; | ||
479 | |||
480 | GNUNET_assert (GNUNET_OK == | ||
481 | GNUNET_CONTAINER_multishortmap_remove (db->channel_map, | ||
482 | &eh->sh, | ||
483 | eh)); | ||
484 | if (NULL == | ||
485 | GNUNET_CONTAINER_multishortmap_get (db->channel_map, | ||
486 | &eh->sh)) | ||
487 | manage_subscribe (db, | ||
488 | "UNLISTEN X", | ||
489 | eh); | ||
490 | if (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map)) | ||
491 | { | ||
492 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
493 | "Stopping PQ event scheduler job\n"); | ||
494 | GNUNET_free (db->rfd); | ||
495 | if (NULL != db->event_task) | ||
496 | { | ||
497 | GNUNET_SCHEDULER_cancel (db->event_task); | ||
498 | db->event_task = NULL; | ||
499 | } | ||
500 | } | ||
501 | if (NULL != eh->timeout_task) | ||
502 | { | ||
503 | GNUNET_SCHEDULER_cancel (eh->timeout_task); | ||
504 | eh->timeout_task = NULL; | ||
505 | } | ||
506 | GNUNET_free (eh); | ||
507 | } | ||
508 | |||
509 | |||
510 | void | ||
511 | GNUNET_PQ_event_notify (struct GNUNET_PQ_Context *db, | ||
512 | const struct GNUNET_DB_EventHeaderP *es, | ||
513 | const void *extra, | ||
514 | size_t extra_size) | ||
515 | { | ||
516 | char sql[16 + 64 + extra_size * 8 / 5 + 8]; | ||
517 | char *end; | ||
518 | PGresult *result; | ||
519 | |||
520 | end = stpcpy (sql, | ||
521 | "NOTIFY X"); | ||
522 | end = es_to_channel (es, | ||
523 | end); | ||
524 | end = stpcpy (end, | ||
525 | ", '"); | ||
526 | end = GNUNET_STRINGS_data_to_string (extra, | ||
527 | extra_size, | ||
528 | end, | ||
529 | sizeof (sql) - (end - sql) - 1); | ||
530 | GNUNET_assert (NULL != end); | ||
531 | *end = '\0'; | ||
532 | end = stpcpy (end, | ||
533 | "'"); | ||
534 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
535 | "Executing command `%s'\n", | ||
536 | sql); | ||
537 | result = PQexec (db->conn, | ||
538 | sql); | ||
539 | if (PGRES_COMMAND_OK != PQresultStatus (result)) | ||
540 | { | ||
541 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, | ||
542 | "pq", | ||
543 | "Failed to execute `%s': %s/%s/%s/%s/%s", | ||
544 | sql, | ||
545 | PQresultErrorField (result, | ||
546 | PG_DIAG_MESSAGE_PRIMARY), | ||
547 | PQresultErrorField (result, | ||
548 | PG_DIAG_MESSAGE_DETAIL), | ||
549 | PQresultErrorMessage (result), | ||
550 | PQresStatus (PQresultStatus (result)), | ||
551 | PQerrorMessage (db->conn)); | ||
552 | } | ||
553 | PQclear (result); | ||
554 | event_do_poll (db); | ||
555 | } | ||
556 | |||
557 | |||
558 | /* end of pq_event.c */ | ||
diff --git a/src/pq/pq_exec.c b/src/pq/pq_exec.c deleted file mode 100644 index dcde331b6..000000000 --- a/src/pq/pq_exec.c +++ /dev/null | |||
@@ -1,117 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2017, 2019 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file pq/pq_exec.c | ||
22 | * @brief functions to execute plain SQL statements (PostGres) | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "pq.h" | ||
27 | |||
28 | |||
29 | /** | ||
30 | * Create a `struct GNUNET_PQ_ExecuteStatement` where errors are fatal. | ||
31 | * | ||
32 | * @param sql actual SQL statement | ||
33 | * @return initialized struct | ||
34 | */ | ||
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 76fc5c374..000000000 --- a/src/pq/pq_prepare.c +++ /dev/null | |||
@@ -1,122 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2017, 2019 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file pq/pq_prepare.c | ||
22 | * @brief functions to connect to libpq (PostGres) | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "pq.h" | ||
27 | |||
28 | |||
29 | /** | ||
30 | * Create a `struct GNUNET_PQ_PreparedStatement`. | ||
31 | * | ||
32 | * @param name name of the statement | ||
33 | * @param sql actual SQL statement | ||
34 | * @param num_args number of arguments in the statement | ||
35 | * @return initialized struct | ||
36 | */ | ||
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 | return GNUNET_SYSERR; | ||
115 | } | ||
116 | PQclear (ret); | ||
117 | } | ||
118 | return GNUNET_OK; | ||
119 | } | ||
120 | |||
121 | |||
122 | /* end of pq/pq_prepare.c */ | ||
diff --git a/src/pq/pq_query_helper.c b/src/pq/pq_query_helper.c deleted file mode 100644 index cee84d203..000000000 --- a/src/pq/pq_query_helper.c +++ /dev/null | |||
@@ -1,520 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2014, 2015, 2016, 2020 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file pq/pq_query_helper.c | ||
22 | * @brief functions to initialize parameter arrays | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_util_lib.h" | ||
27 | #include "gnunet_pq_lib.h" | ||
28 | |||
29 | |||
30 | /** | ||
31 | * Function called to convert input argument into SQL parameters. | ||
32 | * | ||
33 | * @param cls closure | ||
34 | * @param data pointer to input argument | ||
35 | * @param data_len number of bytes in @a data (if applicable) | ||
36 | * @param[out] param_values SQL data to set | ||
37 | * @param[out] param_lengths SQL length data to set | ||
38 | * @param[out] param_formats SQL format data to set | ||
39 | * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays | ||
40 | * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() | ||
41 | * @param scratch_length number of entries left in @a scratch | ||
42 | * @return -1 on error, number of offsets used in @a scratch otherwise | ||
43 | */ | ||
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 | &qconv_null, NULL, NULL, 0, 1 | ||
74 | }; | ||
75 | |||
76 | return res; | ||
77 | } | ||
78 | |||
79 | |||
80 | /** | ||
81 | * Function called to convert input argument into SQL parameters. | ||
82 | * | ||
83 | * @param cls closure | ||
84 | * @param data pointer to input argument | ||
85 | * @param data_len number of bytes in @a data (if applicable) | ||
86 | * @param[out] param_values SQL data to set | ||
87 | * @param[out] param_lengths SQL length data to set | ||
88 | * @param[out] param_formats SQL format data to set | ||
89 | * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays | ||
90 | * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() | ||
91 | * @param scratch_length number of entries left in @a scratch | ||
92 | * @return -1 on error, number of offsets used in @a scratch otherwise | ||
93 | */ | ||
94 | static int | ||
95 | qconv_fixed (void *cls, | ||
96 | const void *data, | ||
97 | size_t data_len, | ||
98 | void *param_values[], | ||
99 | int param_lengths[], | ||
100 | int param_formats[], | ||
101 | unsigned int param_length, | ||
102 | void *scratch[], | ||
103 | unsigned int scratch_length) | ||
104 | { | ||
105 | (void) scratch; | ||
106 | (void) scratch_length; | ||
107 | GNUNET_break (NULL == cls); | ||
108 | if (1 != param_length) | ||
109 | return -1; | ||
110 | param_values[0] = (void *) data; | ||
111 | param_lengths[0] = data_len; | ||
112 | param_formats[0] = 1; | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | |||
117 | struct GNUNET_PQ_QueryParam | ||
118 | GNUNET_PQ_query_param_fixed_size (const void *ptr, | ||
119 | size_t ptr_size) | ||
120 | { | ||
121 | struct GNUNET_PQ_QueryParam res = { | ||
122 | &qconv_fixed, NULL, ptr, ptr_size, 1 | ||
123 | }; | ||
124 | |||
125 | return res; | ||
126 | } | ||
127 | |||
128 | |||
129 | struct GNUNET_PQ_QueryParam | ||
130 | GNUNET_PQ_query_param_string (const char *ptr) | ||
131 | { | ||
132 | return GNUNET_PQ_query_param_fixed_size (ptr, | ||
133 | strlen (ptr)); | ||
134 | } | ||
135 | |||
136 | |||
137 | /** | ||
138 | * Function called to convert input argument into SQL parameters. | ||
139 | * | ||
140 | * @param cls closure | ||
141 | * @param data pointer to input argument | ||
142 | * @param data_len number of bytes in @a data (if applicable) | ||
143 | * @param[out] param_values SQL data to set | ||
144 | * @param[out] param_lengths SQL length data to set | ||
145 | * @param[out] param_formats SQL format data to set | ||
146 | * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays | ||
147 | * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() | ||
148 | * @param scratch_length number of entries left in @a scratch | ||
149 | * @return -1 on error, number of offsets used in @a scratch otherwise | ||
150 | */ | ||
151 | static int | ||
152 | qconv_uint16 (void *cls, | ||
153 | const void *data, | ||
154 | size_t data_len, | ||
155 | void *param_values[], | ||
156 | int param_lengths[], | ||
157 | int param_formats[], | ||
158 | unsigned int param_length, | ||
159 | void *scratch[], | ||
160 | unsigned int scratch_length) | ||
161 | { | ||
162 | const uint16_t *u_hbo = data; | ||
163 | uint16_t *u_nbo; | ||
164 | |||
165 | (void) scratch; | ||
166 | (void) scratch_length; | ||
167 | GNUNET_break (NULL == cls); | ||
168 | if (1 != param_length) | ||
169 | return -1; | ||
170 | u_nbo = GNUNET_new (uint16_t); | ||
171 | scratch[0] = u_nbo; | ||
172 | *u_nbo = htons (*u_hbo); | ||
173 | param_values[0] = (void *) u_nbo; | ||
174 | param_lengths[0] = sizeof(uint16_t); | ||
175 | param_formats[0] = 1; | ||
176 | return 1; | ||
177 | } | ||
178 | |||
179 | |||
180 | struct GNUNET_PQ_QueryParam | ||
181 | GNUNET_PQ_query_param_uint16 (const uint16_t *x) | ||
182 | { | ||
183 | struct GNUNET_PQ_QueryParam res = | ||
184 | { &qconv_uint16, NULL, x, sizeof(*x), 1 }; | ||
185 | |||
186 | return res; | ||
187 | } | ||
188 | |||
189 | |||
190 | /** | ||
191 | * Function called to convert input argument into SQL parameters. | ||
192 | * | ||
193 | * @param cls closure | ||
194 | * @param data pointer to input argument | ||
195 | * @param data_len number of bytes in @a data (if applicable) | ||
196 | * @param[out] param_values SQL data to set | ||
197 | * @param[out] param_lengths SQL length data to set | ||
198 | * @param[out] param_formats SQL format data to set | ||
199 | * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays | ||
200 | * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() | ||
201 | * @param scratch_length number of entries left in @a scratch | ||
202 | * @return -1 on error, number of offsets used in @a scratch otherwise | ||
203 | */ | ||
204 | static int | ||
205 | qconv_uint32 (void *cls, | ||
206 | const void *data, | ||
207 | size_t data_len, | ||
208 | void *param_values[], | ||
209 | int param_lengths[], | ||
210 | int param_formats[], | ||
211 | unsigned int param_length, | ||
212 | void *scratch[], | ||
213 | unsigned int scratch_length) | ||
214 | { | ||
215 | const uint32_t *u_hbo = data; | ||
216 | uint32_t *u_nbo; | ||
217 | |||
218 | (void) scratch; | ||
219 | (void) scratch_length; | ||
220 | GNUNET_break (NULL == cls); | ||
221 | if (1 != param_length) | ||
222 | return -1; | ||
223 | u_nbo = GNUNET_new (uint32_t); | ||
224 | scratch[0] = u_nbo; | ||
225 | *u_nbo = htonl (*u_hbo); | ||
226 | param_values[0] = (void *) u_nbo; | ||
227 | param_lengths[0] = sizeof(uint32_t); | ||
228 | param_formats[0] = 1; | ||
229 | return 1; | ||
230 | } | ||
231 | |||
232 | |||
233 | struct GNUNET_PQ_QueryParam | ||
234 | GNUNET_PQ_query_param_uint32 (const uint32_t *x) | ||
235 | { | ||
236 | struct GNUNET_PQ_QueryParam res = | ||
237 | { &qconv_uint32, NULL, x, sizeof(*x), 1 }; | ||
238 | |||
239 | return res; | ||
240 | } | ||
241 | |||
242 | |||
243 | /** | ||
244 | * Function called to convert input argument into SQL parameters. | ||
245 | * | ||
246 | * @param cls closure | ||
247 | * @param data pointer to input argument | ||
248 | * @param data_len number of bytes in @a data (if applicable) | ||
249 | * @param[out] param_values SQL data to set | ||
250 | * @param[out] param_lengths SQL length data to set | ||
251 | * @param[out] param_formats SQL format data to set | ||
252 | * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays | ||
253 | * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() | ||
254 | * @param scratch_length number of entries left in @a scratch | ||
255 | * @return -1 on error, number of offsets used in @a scratch otherwise | ||
256 | */ | ||
257 | static int | ||
258 | qconv_uint64 (void *cls, | ||
259 | const void *data, | ||
260 | size_t data_len, | ||
261 | void *param_values[], | ||
262 | int param_lengths[], | ||
263 | int param_formats[], | ||
264 | unsigned int param_length, | ||
265 | void *scratch[], | ||
266 | unsigned int scratch_length) | ||
267 | { | ||
268 | const uint64_t *u_hbo = data; | ||
269 | uint64_t *u_nbo; | ||
270 | |||
271 | (void) scratch; | ||
272 | (void) scratch_length; | ||
273 | GNUNET_break (NULL == cls); | ||
274 | if (1 != param_length) | ||
275 | return -1; | ||
276 | u_nbo = GNUNET_new (uint64_t); | ||
277 | scratch[0] = u_nbo; | ||
278 | *u_nbo = GNUNET_htonll (*u_hbo); | ||
279 | param_values[0] = (void *) u_nbo; | ||
280 | param_lengths[0] = sizeof(uint64_t); | ||
281 | param_formats[0] = 1; | ||
282 | return 1; | ||
283 | } | ||
284 | |||
285 | |||
286 | struct GNUNET_PQ_QueryParam | ||
287 | GNUNET_PQ_query_param_uint64 (const uint64_t *x) | ||
288 | { | ||
289 | struct GNUNET_PQ_QueryParam res = | ||
290 | { &qconv_uint64, NULL, x, sizeof(*x), 1 }; | ||
291 | |||
292 | return res; | ||
293 | } | ||
294 | |||
295 | |||
296 | /** | ||
297 | * Function called to convert input argument into SQL parameters. | ||
298 | * | ||
299 | * @param cls closure | ||
300 | * @param data pointer to input argument | ||
301 | * @param data_len number of bytes in @a data (if applicable) | ||
302 | * @param[out] param_values SQL data to set | ||
303 | * @param[out] param_lengths SQL length data to set | ||
304 | * @param[out] param_formats SQL format data to set | ||
305 | * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays | ||
306 | * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() | ||
307 | * @param scratch_length number of entries left in @a scratch | ||
308 | * @return -1 on error, number of offsets used in @a scratch otherwise | ||
309 | */ | ||
310 | static int | ||
311 | qconv_rsa_public_key (void *cls, | ||
312 | const void *data, | ||
313 | size_t data_len, | ||
314 | void *param_values[], | ||
315 | int param_lengths[], | ||
316 | int param_formats[], | ||
317 | unsigned int param_length, | ||
318 | void *scratch[], | ||
319 | unsigned int scratch_length) | ||
320 | { | ||
321 | const struct GNUNET_CRYPTO_RsaPublicKey *rsa = data; | ||
322 | void *buf; | ||
323 | size_t buf_size; | ||
324 | |||
325 | GNUNET_break (NULL == cls); | ||
326 | if (1 != param_length) | ||
327 | return -1; | ||
328 | buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rsa, | ||
329 | &buf); | ||
330 | scratch[0] = buf; | ||
331 | param_values[0] = (void *) buf; | ||
332 | param_lengths[0] = buf_size; | ||
333 | param_formats[0] = 1; | ||
334 | return 1; | ||
335 | } | ||
336 | |||
337 | |||
338 | struct GNUNET_PQ_QueryParam | ||
339 | GNUNET_PQ_query_param_rsa_public_key (const struct | ||
340 | GNUNET_CRYPTO_RsaPublicKey *x) | ||
341 | { | ||
342 | struct GNUNET_PQ_QueryParam res = | ||
343 | { &qconv_rsa_public_key, NULL, (x), 0, 1 }; | ||
344 | |||
345 | return res; | ||
346 | } | ||
347 | |||
348 | |||
349 | /** | ||
350 | * Function called to convert input argument into SQL parameters. | ||
351 | * | ||
352 | * @param cls closure | ||
353 | * @param data pointer to input argument | ||
354 | * @param data_len number of bytes in @a data (if applicable) | ||
355 | * @param[out] param_values SQL data to set | ||
356 | * @param[out] param_lengths SQL length data to set | ||
357 | * @param[out] param_formats SQL format data to set | ||
358 | * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays | ||
359 | * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() | ||
360 | * @param scratch_length number of entries left in @a scratch | ||
361 | * @return -1 on error, number of offsets used in @a scratch otherwise | ||
362 | */ | ||
363 | static int | ||
364 | qconv_rsa_signature (void *cls, | ||
365 | const void *data, | ||
366 | size_t data_len, | ||
367 | void *param_values[], | ||
368 | int param_lengths[], | ||
369 | int param_formats[], | ||
370 | unsigned int param_length, | ||
371 | void *scratch[], | ||
372 | unsigned int scratch_length) | ||
373 | { | ||
374 | const struct GNUNET_CRYPTO_RsaSignature *sig = data; | ||
375 | void *buf; | ||
376 | size_t buf_size; | ||
377 | |||
378 | GNUNET_break (NULL == cls); | ||
379 | if (1 != param_length) | ||
380 | return -1; | ||
381 | buf_size = GNUNET_CRYPTO_rsa_signature_encode (sig, | ||
382 | &buf); | ||
383 | scratch[0] = buf; | ||
384 | param_values[0] = (void *) buf; | ||
385 | param_lengths[0] = buf_size; | ||
386 | param_formats[0] = 1; | ||
387 | return 1; | ||
388 | } | ||
389 | |||
390 | |||
391 | struct GNUNET_PQ_QueryParam | ||
392 | GNUNET_PQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_RsaSignature *x) | ||
393 | { | ||
394 | struct GNUNET_PQ_QueryParam res = | ||
395 | { &qconv_rsa_signature, NULL, (x), 0, 1 }; | ||
396 | |||
397 | return res; | ||
398 | } | ||
399 | |||
400 | |||
401 | /** | ||
402 | * Function called to convert input argument into SQL parameters. | ||
403 | * | ||
404 | * @param cls closure | ||
405 | * @param data pointer to input argument | ||
406 | * @param data_len number of bytes in @a data (if applicable) | ||
407 | * @param[out] param_values SQL data to set | ||
408 | * @param[out] param_lengths SQL length data to set | ||
409 | * @param[out] param_formats SQL format data to set | ||
410 | * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays | ||
411 | * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() | ||
412 | * @param scratch_length number of entries left in @a scratch | ||
413 | * @return -1 on error, number of offsets used in @a scratch otherwise | ||
414 | */ | ||
415 | static int | ||
416 | qconv_rel_time (void *cls, | ||
417 | const void *data, | ||
418 | size_t data_len, | ||
419 | void *param_values[], | ||
420 | int param_lengths[], | ||
421 | int param_formats[], | ||
422 | unsigned int param_length, | ||
423 | void *scratch[], | ||
424 | unsigned int scratch_length) | ||
425 | { | ||
426 | const struct GNUNET_TIME_Relative *u = data; | ||
427 | struct GNUNET_TIME_Relative rel; | ||
428 | uint64_t *u_nbo; | ||
429 | |||
430 | GNUNET_break (NULL == cls); | ||
431 | if (1 != param_length) | ||
432 | return -1; | ||
433 | rel = *u; | ||
434 | if (rel.rel_value_us > INT64_MAX) | ||
435 | rel.rel_value_us = INT64_MAX; | ||
436 | u_nbo = GNUNET_new (uint64_t); | ||
437 | scratch[0] = u_nbo; | ||
438 | *u_nbo = GNUNET_htonll (rel.rel_value_us); | ||
439 | param_values[0] = (void *) u_nbo; | ||
440 | param_lengths[0] = sizeof(uint64_t); | ||
441 | param_formats[0] = 1; | ||
442 | return 1; | ||
443 | } | ||
444 | |||
445 | |||
446 | struct GNUNET_PQ_QueryParam | ||
447 | GNUNET_PQ_query_param_relative_time (const struct GNUNET_TIME_Relative *x) | ||
448 | { | ||
449 | struct GNUNET_PQ_QueryParam res = | ||
450 | { &qconv_rel_time, NULL, x, sizeof(*x), 1 }; | ||
451 | |||
452 | return res; | ||
453 | } | ||
454 | |||
455 | |||
456 | /** | ||
457 | * Function called to convert input argument into SQL parameters. | ||
458 | * | ||
459 | * @param cls closure | ||
460 | * @param data pointer to input argument | ||
461 | * @param data_len number of bytes in @a data (if applicable) | ||
462 | * @param[out] param_values SQL data to set | ||
463 | * @param[out] param_lengths SQL length data to set | ||
464 | * @param[out] param_formats SQL format data to set | ||
465 | * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays | ||
466 | * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() | ||
467 | * @param scratch_length number of entries left in @a scratch | ||
468 | * @return -1 on error, number of offsets used in @a scratch otherwise | ||
469 | */ | ||
470 | static int | ||
471 | qconv_abs_time (void *cls, | ||
472 | const void *data, | ||
473 | size_t data_len, | ||
474 | void *param_values[], | ||
475 | int param_lengths[], | ||
476 | int param_formats[], | ||
477 | unsigned int param_length, | ||
478 | void *scratch[], | ||
479 | unsigned int scratch_length) | ||
480 | { | ||
481 | const struct GNUNET_TIME_Absolute *u = data; | ||
482 | struct GNUNET_TIME_Absolute abs; | ||
483 | uint64_t *u_nbo; | ||
484 | |||
485 | GNUNET_break (NULL == cls); | ||
486 | if (1 != param_length) | ||
487 | return -1; | ||
488 | abs = *u; | ||
489 | if (abs.abs_value_us > INT64_MAX) | ||
490 | abs.abs_value_us = INT64_MAX; | ||
491 | u_nbo = GNUNET_new (uint64_t); | ||
492 | scratch[0] = u_nbo; | ||
493 | *u_nbo = GNUNET_htonll (abs.abs_value_us); | ||
494 | param_values[0] = (void *) u_nbo; | ||
495 | param_lengths[0] = sizeof(uint64_t); | ||
496 | param_formats[0] = 1; | ||
497 | return 1; | ||
498 | } | ||
499 | |||
500 | |||
501 | struct GNUNET_PQ_QueryParam | ||
502 | GNUNET_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x) | ||
503 | { | ||
504 | struct GNUNET_PQ_QueryParam res = { | ||
505 | &qconv_abs_time, NULL, x, sizeof(*x), 1 | ||
506 | }; | ||
507 | |||
508 | return res; | ||
509 | } | ||
510 | |||
511 | |||
512 | struct GNUNET_PQ_QueryParam | ||
513 | GNUNET_PQ_query_param_absolute_time_nbo (const struct | ||
514 | GNUNET_TIME_AbsoluteNBO *x) | ||
515 | { | ||
516 | return GNUNET_PQ_query_param_auto_from_type (&x->abs_value_us__); | ||
517 | } | ||
518 | |||
519 | |||
520 | /* end of pq_query_helper.c */ | ||
diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c deleted file mode 100644 index f264603f4..000000000 --- a/src/pq/pq_result_helper.c +++ /dev/null | |||
@@ -1,907 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | Copyright (C) 2014, 2015, 2016, 2020 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file pq/pq_result_helper.c | ||
22 | * @brief functions to extract result values | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "gnunet_util_lib.h" | ||
27 | #include "gnunet_pq_lib.h" | ||
28 | |||
29 | |||
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 | { &extract_string, | ||
501 | &clean_string, | ||
502 | NULL, | ||
503 | (void *) dst, 0, (name), NULL }; | ||
504 | |||
505 | return res; | ||
506 | } | ||
507 | |||
508 | |||
509 | /** | ||
510 | * Extract data from a Postgres database @a result at row @a row. | ||
511 | * | ||
512 | * @param cls closure | ||
513 | * @param result where to extract data from | ||
514 | * @param int row to extract data from | ||
515 | * @param fname name (or prefix) of the fields to extract from | ||
516 | * @param[in,out] dst_size where to store size of result, may be NULL | ||
517 | * @param[out] dst where to store the result | ||
518 | * @return | ||
519 | * #GNUNET_YES if all results could be extracted | ||
520 | * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) | ||
521 | */ | ||
522 | static enum GNUNET_GenericReturnValue | ||
523 | extract_rel_time (void *cls, | ||
524 | PGresult *result, | ||
525 | int row, | ||
526 | const char *fname, | ||
527 | size_t *dst_size, | ||
528 | void *dst) | ||
529 | { | ||
530 | struct GNUNET_TIME_Relative *udst = dst; | ||
531 | const int64_t *res; | ||
532 | int fnum; | ||
533 | |||
534 | (void) cls; | ||
535 | fnum = PQfnumber (result, | ||
536 | fname); | ||
537 | if (fnum < 0) | ||
538 | { | ||
539 | GNUNET_break (0); | ||
540 | return GNUNET_SYSERR; | ||
541 | } | ||
542 | if (PQgetisnull (result, | ||
543 | row, | ||
544 | fnum)) | ||
545 | return GNUNET_NO; | ||
546 | GNUNET_assert (NULL != dst); | ||
547 | if (sizeof(struct GNUNET_TIME_Relative) != *dst_size) | ||
548 | { | ||
549 | GNUNET_break (0); | ||
550 | return GNUNET_SYSERR; | ||
551 | } | ||
552 | if (sizeof(int64_t) != | ||
553 | PQgetlength (result, | ||
554 | row, | ||
555 | fnum)) | ||
556 | { | ||
557 | GNUNET_break (0); | ||
558 | return GNUNET_SYSERR; | ||
559 | } | ||
560 | res = (int64_t *) PQgetvalue (result, | ||
561 | row, | ||
562 | fnum); | ||
563 | if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res)) | ||
564 | *udst = GNUNET_TIME_UNIT_FOREVER_REL; | ||
565 | else | ||
566 | udst->rel_value_us = GNUNET_ntohll ((uint64_t) *res); | ||
567 | return GNUNET_OK; | ||
568 | } | ||
569 | |||
570 | |||
571 | struct GNUNET_PQ_ResultSpec | ||
572 | GNUNET_PQ_result_spec_relative_time (const char *name, | ||
573 | struct GNUNET_TIME_Relative *rt) | ||
574 | { | ||
575 | struct GNUNET_PQ_ResultSpec res = { | ||
576 | &extract_rel_time, | ||
577 | NULL, | ||
578 | NULL, | ||
579 | (void *) rt, | ||
580 | sizeof(*rt), | ||
581 | name, | ||
582 | NULL | ||
583 | }; | ||
584 | |||
585 | return res; | ||
586 | } | ||
587 | |||
588 | |||
589 | /** | ||
590 | * Extract data from a Postgres database @a result at row @a row. | ||
591 | * | ||
592 | * @param cls closure | ||
593 | * @param result where to extract data from | ||
594 | * @param int row to extract data from | ||
595 | * @param fname name (or prefix) of the fields to extract from | ||
596 | * @param[in,out] dst_size where to store size of result, may be NULL | ||
597 | * @param[out] dst where to store the result | ||
598 | * @return | ||
599 | * #GNUNET_YES if all results could be extracted | ||
600 | * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) | ||
601 | */ | ||
602 | static enum GNUNET_GenericReturnValue | ||
603 | extract_abs_time (void *cls, | ||
604 | PGresult *result, | ||
605 | int row, | ||
606 | const char *fname, | ||
607 | size_t *dst_size, | ||
608 | void *dst) | ||
609 | { | ||
610 | struct GNUNET_TIME_Absolute *udst = dst; | ||
611 | const int64_t *res; | ||
612 | int fnum; | ||
613 | |||
614 | (void) cls; | ||
615 | fnum = PQfnumber (result, | ||
616 | fname); | ||
617 | if (fnum < 0) | ||
618 | { | ||
619 | GNUNET_break (0); | ||
620 | return GNUNET_SYSERR; | ||
621 | } | ||
622 | if (PQgetisnull (result, | ||
623 | row, | ||
624 | fnum)) | ||
625 | return GNUNET_NO; | ||
626 | GNUNET_assert (NULL != dst); | ||
627 | if (sizeof(struct GNUNET_TIME_Absolute) != *dst_size) | ||
628 | { | ||
629 | GNUNET_break (0); | ||
630 | return GNUNET_SYSERR; | ||
631 | } | ||
632 | if (sizeof(int64_t) != | ||
633 | PQgetlength (result, | ||
634 | row, | ||
635 | fnum)) | ||
636 | { | ||
637 | GNUNET_break (0); | ||
638 | return GNUNET_SYSERR; | ||
639 | } | ||
640 | res = (int64_t *) PQgetvalue (result, | ||
641 | row, | ||
642 | fnum); | ||
643 | if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res)) | ||
644 | *udst = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
645 | else | ||
646 | udst->abs_value_us = GNUNET_ntohll ((uint64_t) *res); | ||
647 | return GNUNET_OK; | ||
648 | } | ||
649 | |||
650 | |||
651 | struct GNUNET_PQ_ResultSpec | ||
652 | GNUNET_PQ_result_spec_absolute_time (const char *name, | ||
653 | struct GNUNET_TIME_Absolute *at) | ||
654 | { | ||
655 | struct GNUNET_PQ_ResultSpec res = | ||
656 | { &extract_abs_time, | ||
657 | NULL, | ||
658 | NULL, | ||
659 | (void *) at, sizeof(*at), (name), NULL }; | ||
660 | |||
661 | return res; | ||
662 | } | ||
663 | |||
664 | |||
665 | struct GNUNET_PQ_ResultSpec | ||
666 | GNUNET_PQ_result_spec_absolute_time_nbo (const char *name, | ||
667 | struct GNUNET_TIME_AbsoluteNBO *at) | ||
668 | { | ||
669 | struct GNUNET_PQ_ResultSpec res = | ||
670 | GNUNET_PQ_result_spec_auto_from_type (name, &at->abs_value_us__); | ||
671 | |||
672 | return res; | ||
673 | } | ||
674 | |||
675 | |||
676 | /** | ||
677 | * Extract data from a Postgres database @a result at row @a row. | ||
678 | * | ||
679 | * @param cls closure | ||
680 | * @param result where to extract data from | ||
681 | * @param int row to extract data from | ||
682 | * @param fname name (or prefix) of the fields to extract from | ||
683 | * @param[in,out] dst_size where to store size of result, may be NULL | ||
684 | * @param[out] dst where to store the result | ||
685 | * @return | ||
686 | * #GNUNET_YES if all results could be extracted | ||
687 | * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) | ||
688 | */ | ||
689 | static enum GNUNET_GenericReturnValue | ||
690 | extract_uint16 (void *cls, | ||
691 | PGresult *result, | ||
692 | int row, | ||
693 | const char *fname, | ||
694 | size_t *dst_size, | ||
695 | void *dst) | ||
696 | { | ||
697 | uint16_t *udst = dst; | ||
698 | const uint16_t *res; | ||
699 | int fnum; | ||
700 | |||
701 | (void) cls; | ||
702 | fnum = PQfnumber (result, | ||
703 | fname); | ||
704 | if (fnum < 0) | ||
705 | { | ||
706 | GNUNET_break (0); | ||
707 | return GNUNET_SYSERR; | ||
708 | } | ||
709 | if (PQgetisnull (result, | ||
710 | row, | ||
711 | fnum)) | ||
712 | return GNUNET_NO; | ||
713 | GNUNET_assert (NULL != dst); | ||
714 | if (sizeof(uint16_t) != *dst_size) | ||
715 | { | ||
716 | GNUNET_break (0); | ||
717 | return GNUNET_SYSERR; | ||
718 | } | ||
719 | if (sizeof(uint16_t) != | ||
720 | PQgetlength (result, | ||
721 | row, | ||
722 | fnum)) | ||
723 | { | ||
724 | GNUNET_break (0); | ||
725 | return GNUNET_SYSERR; | ||
726 | } | ||
727 | res = (uint16_t *) PQgetvalue (result, | ||
728 | row, | ||
729 | fnum); | ||
730 | *udst = ntohs (*res); | ||
731 | return GNUNET_OK; | ||
732 | } | ||
733 | |||
734 | |||
735 | struct GNUNET_PQ_ResultSpec | ||
736 | GNUNET_PQ_result_spec_uint16 (const char *name, | ||
737 | uint16_t *u16) | ||
738 | { | ||
739 | struct GNUNET_PQ_ResultSpec res = | ||
740 | { &extract_uint16, | ||
741 | NULL, | ||
742 | NULL, | ||
743 | (void *) u16, sizeof(*u16), (name), NULL }; | ||
744 | |||
745 | return res; | ||
746 | } | ||
747 | |||
748 | |||
749 | /** | ||
750 | * Extract data from a Postgres database @a result at row @a row. | ||
751 | * | ||
752 | * @param cls closure | ||
753 | * @param result where to extract data from | ||
754 | * @param int row to extract data from | ||
755 | * @param fname name (or prefix) of the fields to extract from | ||
756 | * @param[in,out] dst_size where to store size of result, may be NULL | ||
757 | * @param[out] dst where to store the result | ||
758 | * @return | ||
759 | * #GNUNET_YES if all results could be extracted | ||
760 | * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) | ||
761 | */ | ||
762 | static enum GNUNET_GenericReturnValue | ||
763 | extract_uint32 (void *cls, | ||
764 | PGresult *result, | ||
765 | int row, | ||
766 | const char *fname, | ||
767 | size_t *dst_size, | ||
768 | void *dst) | ||
769 | { | ||
770 | uint32_t *udst = dst; | ||
771 | const uint32_t *res; | ||
772 | int fnum; | ||
773 | |||
774 | (void) cls; | ||
775 | fnum = PQfnumber (result, | ||
776 | fname); | ||
777 | if (fnum < 0) | ||
778 | { | ||
779 | GNUNET_break (0); | ||
780 | return GNUNET_SYSERR; | ||
781 | } | ||
782 | if (PQgetisnull (result, | ||
783 | row, | ||
784 | fnum)) | ||
785 | return GNUNET_NO; | ||
786 | GNUNET_assert (NULL != dst); | ||
787 | if (sizeof(uint32_t) != *dst_size) | ||
788 | { | ||
789 | GNUNET_break (0); | ||
790 | return GNUNET_SYSERR; | ||
791 | } | ||
792 | if (sizeof(uint32_t) != | ||
793 | PQgetlength (result, | ||
794 | row, | ||
795 | fnum)) | ||
796 | { | ||
797 | GNUNET_break (0); | ||
798 | return GNUNET_SYSERR; | ||
799 | } | ||
800 | res = (uint32_t *) PQgetvalue (result, | ||
801 | row, | ||
802 | fnum); | ||
803 | *udst = ntohl (*res); | ||
804 | return GNUNET_OK; | ||
805 | } | ||
806 | |||
807 | |||
808 | struct GNUNET_PQ_ResultSpec | ||
809 | GNUNET_PQ_result_spec_uint32 (const char *name, | ||
810 | uint32_t *u32) | ||
811 | { | ||
812 | struct GNUNET_PQ_ResultSpec res = { | ||
813 | &extract_uint32, | ||
814 | NULL, | ||
815 | NULL, | ||
816 | (void *) u32, | ||
817 | sizeof(*u32), | ||
818 | (name), | ||
819 | NULL | ||
820 | }; | ||
821 | |||
822 | return res; | ||
823 | } | ||
824 | |||
825 | |||
826 | /** | ||
827 | * Extract data from a Postgres database @a result at row @a row. | ||
828 | * | ||
829 | * @param cls closure | ||
830 | * @param result where to extract data from | ||
831 | * @param int row to extract data from | ||
832 | * @param fname name (or prefix) of the fields to extract from | ||
833 | * @param[in,out] dst_size where to store size of result, may be NULL | ||
834 | * @param[out] dst where to store the result | ||
835 | * @return | ||
836 | * #GNUNET_YES if all results could be extracted | ||
837 | * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) | ||
838 | */ | ||
839 | static enum GNUNET_GenericReturnValue | ||
840 | extract_uint64 (void *cls, | ||
841 | PGresult *result, | ||
842 | int row, | ||
843 | const char *fname, | ||
844 | size_t *dst_size, | ||
845 | void *dst) | ||
846 | { | ||
847 | uint64_t *udst = dst; | ||
848 | const uint64_t *res; | ||
849 | int fnum; | ||
850 | |||
851 | (void) cls; | ||
852 | fnum = PQfnumber (result, | ||
853 | fname); | ||
854 | if (fnum < 0) | ||
855 | { | ||
856 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
857 | "Field %s missing in result\n", | ||
858 | fname); | ||
859 | GNUNET_break (0); | ||
860 | return GNUNET_SYSERR; | ||
861 | } | ||
862 | if (PQgetisnull (result, | ||
863 | row, | ||
864 | fnum)) | ||
865 | return GNUNET_NO; | ||
866 | |||
867 | GNUNET_assert (NULL != dst); | ||
868 | if (sizeof(uint64_t) != *dst_size) | ||
869 | { | ||
870 | GNUNET_break (0); | ||
871 | return GNUNET_SYSERR; | ||
872 | } | ||
873 | if (sizeof(uint64_t) != | ||
874 | PQgetlength (result, | ||
875 | row, | ||
876 | fnum)) | ||
877 | { | ||
878 | GNUNET_break (0); | ||
879 | return GNUNET_SYSERR; | ||
880 | } | ||
881 | res = (uint64_t *) PQgetvalue (result, | ||
882 | row, | ||
883 | fnum); | ||
884 | *udst = GNUNET_ntohll (*res); | ||
885 | return GNUNET_OK; | ||
886 | } | ||
887 | |||
888 | |||
889 | struct GNUNET_PQ_ResultSpec | ||
890 | GNUNET_PQ_result_spec_uint64 (const char *name, | ||
891 | uint64_t *u64) | ||
892 | { | ||
893 | struct GNUNET_PQ_ResultSpec res = { | ||
894 | &extract_uint64, | ||
895 | NULL, | ||
896 | NULL, | ||
897 | (void *) u64, | ||
898 | sizeof(*u64), | ||
899 | (name), | ||
900 | NULL | ||
901 | }; | ||
902 | |||
903 | return res; | ||
904 | } | ||
905 | |||
906 | |||
907 | /* end of pq_result_helper.c */ | ||
diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c deleted file mode 100644 index 90b5c6489..000000000 --- a/src/pq/test_pq.c +++ /dev/null | |||
@@ -1,421 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | (C) 2015, 2016, 2019, 2020 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file pq/test_pq.c | ||
22 | * @brief Tests for Postgres convenience API | ||
23 | * @author Christian Grothoff <christian@grothoff.org> | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "pq.h" | ||
27 | |||
28 | /** | ||
29 | * Database handle. | ||
30 | */ | ||
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 */ | ||