summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2017-06-03 23:33:43 +0200
committerChristian Grothoff <christian@grothoff.org>2017-06-03 23:33:55 +0200
commit8f254866dc993d2e832ef12ea7b4179f87ab3bd9 (patch)
treeebc1ced68bb48f74f189dadd4b29ff5119849224
parent1a90e7878a8fa1f5b30421bd3045cf5d6d3e13e4 (diff)
downloadgnunet-8f254866dc993d2e832ef12ea7b4179f87ab3bd9.tar.gz
gnunet-8f254866dc993d2e832ef12ea7b4179f87ab3bd9.zip
more pq work
-rw-r--r--src/datastore/plugin_datastore_postgres.c348
-rw-r--r--src/include/gnunet_pq_lib.h6
-rw-r--r--src/pq/pq_eval.c15
3 files changed, 154 insertions, 215 deletions
diff --git a/src/datastore/plugin_datastore_postgres.c b/src/datastore/plugin_datastore_postgres.c
index 1c9ded4f4..7496aeacc 100644
--- a/src/datastore/plugin_datastore_postgres.c
+++ b/src/datastore/plugin_datastore_postgres.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet 2 This file is part of GNUnet
3 Copyright (C) 2009-2016 GNUnet e.V. 3 Copyright (C) 2009-2017 GNUnet e.V.
4 4
5 GNUnet is free software; you can redistribute it and/or modify 5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published 6 it under the terms of the GNU General Public License as published
@@ -70,161 +70,103 @@ struct Plugin
70static int 70static int
71init_connection (struct Plugin *plugin) 71init_connection (struct Plugin *plugin)
72{ 72{
73 PGresult *ret; 73 struct GNUNET_PQ_ExecuteStatement es[] = {
74 /* FIXME: PostgreSQL does not have unsigned integers! This is ok for the type column because
75 * we only test equality on it and can cast it to/from uint32_t. For repl, prio, and anonLevel
76 * we do math or inequality tests, so we can't handle the entire range of uint32_t.
77 * This will also cause problems for expiration times after 294247-01-10-04:00:54 UTC.
78 * PostgreSQL also recommends against using WITH OIDS.
79 */
80 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS gn090 ("
81 " repl INTEGER NOT NULL DEFAULT 0,"
82 " type INTEGER NOT NULL DEFAULT 0,"
83 " prio INTEGER NOT NULL DEFAULT 0,"
84 " anonLevel INTEGER NOT NULL DEFAULT 0,"
85 " expire BIGINT NOT NULL DEFAULT 0,"
86 " rvalue BIGINT NOT NULL DEFAULT 0,"
87 " hash BYTEA NOT NULL DEFAULT '',"
88 " vhash BYTEA NOT NULL DEFAULT '',"
89 " value BYTEA NOT NULL DEFAULT '')"
90 "WITH OIDS"),
91 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS idx_hash ON gn090 (hash)"),
92 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS idx_prio ON gn090 (prio)"),
93 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS idx_expire ON gn090 (expire)"),
94 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS idx_prio_anon ON gn090 (prio,anonLevel)"),
95 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS idx_prio_hash_anon ON gn090 (prio,hash,anonLevel)"),
96 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS idx_repl_rvalue ON gn090 (repl,rvalue)"),
97 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS idx_expire_hash ON gn090 (expire,hash)"),
98 GNUNET_PQ_make_execute ("ALTER TABLE gn090 ALTER value SET STORAGE EXTERNAL"),
99 GNUNET_PQ_make_execute ("ALTER TABLE gn090 ALTER hash SET STORAGE PLAIN"),
100 GNUNET_PQ_make_execute ("ALTER TABLE gn090 ALTER vhash SET STORAGE PLAIN"),
101 GNUNET_PQ_EXECUTE_STATEMENT_END
102 };
103#define RESULT_COLUMNS "repl, type, prio, anonLevel, expire, hash, value, oid"
104 struct GNUNET_PQ_PreparedStatement ps[] = {
105 GNUNET_PQ_make_prepare ("get",
106 "SELECT " RESULT_COLUMNS " FROM gn090 "
107 "WHERE oid >= $1::bigint AND "
108 "(rvalue >= $2 OR 0 = $3::smallint) AND "
109 "(hash = $4 OR 0 = $5::smallint) AND "
110 "(type = $6 OR 0 = $7::smallint) "
111 "ORDER BY oid ASC LIMIT 1",
112 7),
113 GNUNET_PQ_make_prepare ("put",
114 "INSERT INTO gn090 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) "
115 "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
116 9),
117 GNUNET_PQ_make_prepare ("update",
118 "UPDATE gn090 "
119 "SET prio = prio + $1, "
120 "repl = repl + $2, "
121 "expire = GREATEST(expire, $3) "
122 "WHERE hash = $4 AND vhash = $5",
123 5),
124 GNUNET_PQ_make_prepare ("decrepl",
125 "UPDATE gn090 SET repl = GREATEST (repl - 1, 0) "
126 "WHERE oid = $1",
127 1),
128 GNUNET_PQ_make_prepare ("select_non_anonymous",
129 "SELECT " RESULT_COLUMNS " FROM gn090 "
130 "WHERE anonLevel = 0 AND type = $1 AND oid >= $2::bigint "
131 "ORDER BY oid ASC LIMIT 1",
132 2),
133 GNUNET_PQ_make_prepare ("select_expiration_order",
134 "(SELECT " RESULT_COLUMNS " FROM gn090 "
135 "WHERE expire < $1 ORDER BY prio ASC LIMIT 1) "
136 "UNION "
137 "(SELECT " RESULT_COLUMNS " FROM gn090 "
138 "ORDER BY prio ASC LIMIT 1) "
139 "ORDER BY expire ASC LIMIT 1",
140 1),
141 GNUNET_PQ_make_prepare ("select_replication_order",
142 "SELECT " RESULT_COLUMNS " FROM gn090 "
143 "ORDER BY repl DESC,RANDOM() LIMIT 1",
144 0),
145 GNUNET_PQ_make_prepare ("delrow",
146 "DELETE FROM gn090 " "WHERE oid=$1",
147 1),
148 GNUNET_PQ_make_prepare ("remove", "DELETE FROM gn090 "
149 "WHERE hash = $1 AND "
150 "value = $2",
151 2),
152 GNUNET_PQ_make_prepare ("get_keys",
153 "SELECT hash FROM gn090",
154 0),
155 GNUNET_PQ_PREPARED_STATEMENT_END
156 };
157#undef RESULT_COLUMNS
74 158
75 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg, 159 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg,
76 "datastore-postgres"); 160 "datastore-postgres");
77 if (NULL == plugin->dbh) 161 if (NULL == plugin->dbh)
78 return GNUNET_SYSERR; 162 return GNUNET_SYSERR;
79 163
80 /* FIXME: PostgreSQL does not have unsigned integers! This is ok for the type column because 164 if ( (GNUNET_OK !=
81 * we only test equality on it and can cast it to/from uint32_t. For repl, prio, and anonLevel 165 GNUNET_PQ_exec_statements (plugin->dbh,
82 * we do math or inequality tests, so we can't handle the entire range of uint32_t. 166 es)) ||
83 * This will also cause problems for expiration times after 294247-01-10-04:00:54 UTC. 167 (GNUNET_OK !=
84 * PostgreSQL also recommends against using WITH OIDS. 168 GNUNET_PQ_prepare_statements (plugin->dbh,
85 */ 169 ps)) )
86 ret =
87 PQexec (plugin->dbh,
88 "CREATE TABLE IF NOT EXISTS gn090 ("
89 " repl INTEGER NOT NULL DEFAULT 0,"
90 " type INTEGER NOT NULL DEFAULT 0,"
91 " prio INTEGER NOT NULL DEFAULT 0,"
92 " anonLevel INTEGER NOT NULL DEFAULT 0,"
93 " expire BIGINT NOT NULL DEFAULT 0,"
94 " rvalue BIGINT NOT NULL DEFAULT 0,"
95 " hash BYTEA NOT NULL DEFAULT '',"
96 " vhash BYTEA NOT NULL DEFAULT '',"
97 " value BYTEA NOT NULL DEFAULT '')"
98 "WITH OIDS");
99 if ( (NULL == ret) ||
100 ((PQresultStatus (ret) != PGRES_COMMAND_OK) &&
101 (0 != strcmp ("42P07", /* duplicate table */
102 PQresultErrorField
103 (ret,
104 PG_DIAG_SQLSTATE)))))
105 {
106 (void) GNUNET_POSTGRES_check_result (plugin->dbh,
107 ret,
108 PGRES_COMMAND_OK,
109 "CREATE TABLE",
110 "gn090");
111 PQfinish (plugin->dbh);
112 plugin->dbh = NULL;
113 return GNUNET_SYSERR;
114 }
115
116 if (PQresultStatus (ret) == PGRES_COMMAND_OK)
117 {
118 if ((GNUNET_OK !=
119 GNUNET_POSTGRES_exec (plugin->dbh,
120 "CREATE INDEX IF NOT EXISTS idx_hash ON gn090 (hash)")) ||
121 (GNUNET_OK !=
122 GNUNET_POSTGRES_exec (plugin->dbh,
123 "CREATE INDEX IF NOT EXISTS idx_prio ON gn090 (prio)")) ||
124 (GNUNET_OK !=
125 GNUNET_POSTGRES_exec (plugin->dbh,
126 "CREATE INDEX IF NOT EXISTS idx_expire ON gn090 (expire)")) ||
127 (GNUNET_OK !=
128 GNUNET_POSTGRES_exec (plugin->dbh,
129 "CREATE INDEX IF NOT EXISTS idx_prio_anon ON gn090 (prio,anonLevel)")) ||
130 (GNUNET_OK !=
131 GNUNET_POSTGRES_exec (plugin->dbh,
132 "CREATE INDEX IF NOT EXISTS idx_prio_hash_anon ON gn090 (prio,hash,anonLevel)")) ||
133 (GNUNET_OK !=
134 GNUNET_POSTGRES_exec (plugin->dbh,
135 "CREATE INDEX IF NOT EXISTS idx_repl_rvalue ON gn090 (repl,rvalue)")) ||
136 (GNUNET_OK !=
137 GNUNET_POSTGRES_exec (plugin->dbh,
138 "CREATE INDEX IF NOT EXISTS idx_expire_hash ON gn090 (expire,hash)")))
139 {
140 PQclear (ret);
141 PQfinish (plugin->dbh);
142 plugin->dbh = NULL;
143 return GNUNET_SYSERR;
144 }
145 }
146 PQclear (ret);
147
148 ret =
149 PQexec (plugin->dbh,
150 "ALTER TABLE gn090 ALTER value SET STORAGE EXTERNAL");
151 if (GNUNET_OK !=
152 GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn090"))
153 {
154 PQfinish (plugin->dbh);
155 plugin->dbh = NULL;
156 return GNUNET_SYSERR;
157 }
158 PQclear (ret);
159 ret = PQexec (plugin->dbh, "ALTER TABLE gn090 ALTER hash SET STORAGE PLAIN");
160 if (GNUNET_OK !=
161 GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn090"))
162 {
163 PQfinish (plugin->dbh);
164 plugin->dbh = NULL;
165 return GNUNET_SYSERR;
166 }
167 PQclear (ret);
168 ret = PQexec (plugin->dbh, "ALTER TABLE gn090 ALTER vhash SET STORAGE PLAIN");
169 if (GNUNET_OK !=
170 GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn090"))
171 {
172 PQfinish (plugin->dbh);
173 plugin->dbh = NULL;
174 return GNUNET_SYSERR;
175 }
176 PQclear (ret);
177#define RESULT_COLUMNS "repl, type, prio, anonLevel, expire, hash, value, oid"
178 if ((GNUNET_OK !=
179 GNUNET_POSTGRES_prepare (plugin->dbh, "get",
180 "SELECT " RESULT_COLUMNS " FROM gn090 "
181 "WHERE oid >= $1::bigint AND "
182 "(rvalue >= $2 OR 0 = $3::smallint) AND "
183 "(hash = $4 OR 0 = $5::smallint) AND "
184 "(type = $6 OR 0 = $7::smallint) "
185 "ORDER BY oid ASC LIMIT 1", 7)) ||
186 (GNUNET_OK !=
187 GNUNET_POSTGRES_prepare (plugin->dbh, "put",
188 "INSERT INTO gn090 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) "
189 "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", 9)) ||
190 (GNUNET_OK !=
191 GNUNET_POSTGRES_prepare (plugin->dbh, "update",
192 "UPDATE gn090 "
193 "SET prio = prio + $1, "
194 "repl = repl + $2, "
195 "expire = GREATEST(expire, $3) "
196 "WHERE hash = $4 AND vhash = $5", 5)) ||
197 (GNUNET_OK !=
198 GNUNET_POSTGRES_prepare (plugin->dbh, "decrepl",
199 "UPDATE gn090 SET repl = GREATEST (repl - 1, 0) "
200 "WHERE oid = $1", 1)) ||
201 (GNUNET_OK !=
202 GNUNET_POSTGRES_prepare (plugin->dbh, "select_non_anonymous",
203 "SELECT " RESULT_COLUMNS " FROM gn090 "
204 "WHERE anonLevel = 0 AND type = $1 AND oid >= $2::bigint "
205 "ORDER BY oid ASC LIMIT 1",
206 2)) ||
207 (GNUNET_OK !=
208 GNUNET_POSTGRES_prepare (plugin->dbh, "select_expiration_order",
209 "(SELECT " RESULT_COLUMNS " FROM gn090 "
210 "WHERE expire < $1 ORDER BY prio ASC LIMIT 1) "
211 "UNION "
212 "(SELECT " RESULT_COLUMNS " FROM gn090 "
213 "ORDER BY prio ASC LIMIT 1) "
214 "ORDER BY expire ASC LIMIT 1",
215 1)) ||
216 (GNUNET_OK !=
217 GNUNET_POSTGRES_prepare (plugin->dbh, "select_replication_order",
218 "SELECT " RESULT_COLUMNS " FROM gn090 "
219 "ORDER BY repl DESC,RANDOM() LIMIT 1", 0)) ||
220 (GNUNET_OK !=
221 GNUNET_POSTGRES_prepare (plugin->dbh, "delrow", "DELETE FROM gn090 " "WHERE oid=$1", 1)) ||
222 (GNUNET_OK !=
223 GNUNET_POSTGRES_prepare (plugin->dbh, "remove", "DELETE FROM gn090 "
224 "WHERE hash = $1 AND "
225 "value = $2", 2)) ||
226 (GNUNET_OK !=
227 GNUNET_POSTGRES_prepare (plugin->dbh, "get_keys", "SELECT hash FROM gn090", 0)))
228 { 170 {
229 PQfinish (plugin->dbh); 171 PQfinish (plugin->dbh);
230 plugin->dbh = NULL; 172 plugin->dbh = NULL;
@@ -316,13 +258,12 @@ postgres_plugin_put (void *cls,
316{ 258{
317 struct Plugin *plugin = cls; 259 struct Plugin *plugin = cls;
318 struct GNUNET_HashCode vhash; 260 struct GNUNET_HashCode vhash;
319 PGresult *ret; 261 enum GNUNET_PQ_QueryStatus ret;
320 262
321 GNUNET_CRYPTO_hash (data, 263 GNUNET_CRYPTO_hash (data,
322 size, 264 size,
323 &vhash); 265 &vhash);
324 266 if (! absent)
325 if (!absent)
326 { 267 {
327 struct GNUNET_PQ_QueryParam params[] = { 268 struct GNUNET_PQ_QueryParam params[] = {
328 GNUNET_PQ_query_param_uint32 (&priority), 269 GNUNET_PQ_query_param_uint32 (&priority),
@@ -332,15 +273,10 @@ postgres_plugin_put (void *cls,
332 GNUNET_PQ_query_param_auto_from_type (&vhash), 273 GNUNET_PQ_query_param_auto_from_type (&vhash),
333 GNUNET_PQ_query_param_end 274 GNUNET_PQ_query_param_end
334 }; 275 };
335 ret = GNUNET_PQ_exec_prepared (plugin->dbh, 276 ret = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
336 "update", 277 "update",
337 params); 278 params);
338 if (GNUNET_OK != 279 if (0 > ret)
339 GNUNET_POSTGRES_check_result (plugin->dbh,
340 ret,
341 PGRES_COMMAND_OK,
342 "PQexecPrepared",
343 "update"))
344 { 280 {
345 cont (cont_cls, 281 cont (cont_cls,
346 key, 282 key,
@@ -349,9 +285,7 @@ postgres_plugin_put (void *cls,
349 _("Postgress exec failure")); 285 _("Postgress exec failure"));
350 return; 286 return;
351 } 287 }
352 /* What an awful API, this function really does return a string */ 288 bool affected = (0 != ret);
353 bool affected = 0 != strcmp ("0", PQcmdTuples (ret));
354 PQclear (ret);
355 if (affected) 289 if (affected)
356 { 290 {
357 cont (cont_cls, 291 cont (cont_cls,
@@ -363,44 +297,47 @@ postgres_plugin_put (void *cls,
363 } 297 }
364 } 298 }
365 299
366 uint32_t utype = type;
367 uint64_t rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
368 UINT64_MAX);
369 struct GNUNET_PQ_QueryParam params[] = {
370 GNUNET_PQ_query_param_uint32 (&replication),
371 GNUNET_PQ_query_param_uint32 (&utype),
372 GNUNET_PQ_query_param_uint32 (&priority),
373 GNUNET_PQ_query_param_uint32 (&anonymity),
374 GNUNET_PQ_query_param_absolute_time (&expiration),
375 GNUNET_PQ_query_param_uint64 (&rvalue),
376 GNUNET_PQ_query_param_auto_from_type (key),
377 GNUNET_PQ_query_param_auto_from_type (&vhash),
378 GNUNET_PQ_query_param_fixed_size (data, size),
379 GNUNET_PQ_query_param_end
380 };
381
382 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
383 "put",
384 params);
385 if (GNUNET_OK !=
386 GNUNET_POSTGRES_check_result (plugin->dbh,
387 ret,
388 PGRES_COMMAND_OK,
389 "PQexecPrepared", "put"))
390 { 300 {
391 cont (cont_cls, key, size, 301 uint32_t utype = (uint32_t) type;
392 GNUNET_SYSERR, 302 uint64_t rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
393 _("Postgress exec failure")); 303 UINT64_MAX);
394 return; 304 struct GNUNET_PQ_QueryParam params[] = {
305 GNUNET_PQ_query_param_uint32 (&replication),
306 GNUNET_PQ_query_param_uint32 (&utype),
307 GNUNET_PQ_query_param_uint32 (&priority),
308 GNUNET_PQ_query_param_uint32 (&anonymity),
309 GNUNET_PQ_query_param_absolute_time (&expiration),
310 GNUNET_PQ_query_param_uint64 (&rvalue),
311 GNUNET_PQ_query_param_auto_from_type (key),
312 GNUNET_PQ_query_param_auto_from_type (&vhash),
313 GNUNET_PQ_query_param_fixed_size (data, size),
314 GNUNET_PQ_query_param_end
315 };
316
317 ret = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
318 "put",
319 params);
320 if (0 > ret)
321 {
322 cont (cont_cls,
323 key,
324 size,
325 GNUNET_SYSERR,
326 "Postgress exec failure");
327 return;
328 }
395 } 329 }
396 PQclear (ret);
397 plugin->env->duc (plugin->env->cls, 330 plugin->env->duc (plugin->env->cls,
398 size + GNUNET_DATASTORE_ENTRY_OVERHEAD); 331 size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
399 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 332 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
400 "datastore-postgres", 333 "datastore-postgres",
401 "Stored %u bytes in database\n", 334 "Stored %u bytes in database\n",
402 (unsigned int) size); 335 (unsigned int) size);
403 cont (cont_cls, key, size, GNUNET_OK, NULL); 336 cont (cont_cls,
337 key,
338 size,
339 GNUNET_OK,
340 NULL);
404} 341}
405 342
406 343
@@ -864,21 +801,17 @@ postgres_plugin_remove_key (void *cls,
864 void *cont_cls) 801 void *cont_cls)
865{ 802{
866 struct Plugin *plugin = cls; 803 struct Plugin *plugin = cls;
867 PGresult *ret; 804 enum GNUNET_PQ_QueryStatus ret;
868 struct GNUNET_PQ_QueryParam params[] = { 805 struct GNUNET_PQ_QueryParam params[] = {
869 GNUNET_PQ_query_param_auto_from_type (key), 806 GNUNET_PQ_query_param_auto_from_type (key),
870 GNUNET_PQ_query_param_fixed_size (data, size), 807 GNUNET_PQ_query_param_fixed_size (data, size),
871 GNUNET_PQ_query_param_end 808 GNUNET_PQ_query_param_end
872 }; 809 };
873 ret = GNUNET_PQ_exec_prepared (plugin->dbh, 810
874 "remove", 811 ret = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
875 params); 812 "remove",
876 if (GNUNET_OK != 813 params);
877 GNUNET_POSTGRES_check_result (plugin->dbh, 814 if (0 > ret)
878 ret,
879 PGRES_COMMAND_OK,
880 "PQexecPrepared",
881 "remove"))
882 { 815 {
883 cont (cont_cls, 816 cont (cont_cls,
884 key, 817 key,
@@ -887,10 +820,7 @@ postgres_plugin_remove_key (void *cls,
887 _("Postgress exec failure")); 820 _("Postgress exec failure"));
888 return; 821 return;
889 } 822 }
890 /* What an awful API, this function really does return a string */ 823 if (GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS == ret)
891 bool affected = 0 != strcmp ("0", PQcmdTuples (ret));
892 PQclear (ret);
893 if (!affected)
894 { 824 {
895 cont (cont_cls, 825 cont (cont_cls,
896 key, 826 key,
diff --git a/src/include/gnunet_pq_lib.h b/src/include/gnunet_pq_lib.h
index 5e54813e3..ff4df563d 100644
--- a/src/include/gnunet_pq_lib.h
+++ b/src/include/gnunet_pq_lib.h
@@ -523,9 +523,9 @@ GNUNET_PQ_eval_result (PGconn *connection,
523 * @param statement_name name of the statement 523 * @param statement_name name of the statement
524 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated) 524 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
525 * @return status code from the result, mapping PQ status 525 * @return status code from the result, mapping PQ status
526 * codes to `enum GNUNET_PQ_QueryStatus`. Never 526 * codes to `enum GNUNET_PQ_QueryStatus`. If the
527 * returns positive values as this function does 527 * statement was a DELETE or UPDATE statement, the
528 * not look at the result set. 528 * number of affected rows is returned.
529 */ 529 */
530enum GNUNET_PQ_QueryStatus 530enum GNUNET_PQ_QueryStatus
531GNUNET_PQ_eval_prepared_non_select (PGconn *connection, 531GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
diff --git a/src/pq/pq_eval.c b/src/pq/pq_eval.c
index d6c10e2c5..9296dce2e 100644
--- a/src/pq/pq_eval.c
+++ b/src/pq/pq_eval.c
@@ -119,9 +119,9 @@ GNUNET_PQ_eval_result (PGconn *connection,
119 * @param statement_name name of the statement 119 * @param statement_name name of the statement
120 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated) 120 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
121 * @return status code from the result, mapping PQ status 121 * @return status code from the result, mapping PQ status
122 * codes to `enum GNUNET_PQ_QueryStatus`. Never 122 * codes to `enum GNUNET_PQ_QueryStatus`. If the
123 * returns positive values as this function does 123 * statement was a DELETE or UPDATE statement, the
124 * not look at the result set. 124 * number of affected rows is returned.
125 */ 125 */
126enum GNUNET_PQ_QueryStatus 126enum GNUNET_PQ_QueryStatus
127GNUNET_PQ_eval_prepared_non_select (PGconn *connection, 127GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
@@ -137,6 +137,15 @@ GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
137 qs = GNUNET_PQ_eval_result (connection, 137 qs = GNUNET_PQ_eval_result (connection,
138 statement_name, 138 statement_name,
139 result); 139 result);
140 if (GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS == qs)
141 {
142 const char *tuples;
143
144 /* What an awful API, this function really does return a string */
145 tuples = PQcmdTuples (result);
146 if (NULL != tuples)
147 qs = strtol (tuples, NULL, 10);
148 }
140 PQclear (result); 149 PQclear (result);
141 return qs; 150 return qs;
142} 151}