aboutsummaryrefslogtreecommitdiff
path: root/src/datastore
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2010-08-25 19:10:18 +0000
committerChristian Grothoff <christian@grothoff.org>2010-08-25 19:10:18 +0000
commit9aa22137e2427e1f44c7fca2f30005ba8b22195d (patch)
tree29ce98471d30a686c101ffff10b3461f7044491c /src/datastore
parent073be523fd1121b2bb4cd2f77f3098b0c98c1738 (diff)
downloadgnunet-9aa22137e2427e1f44c7fca2f30005ba8b22195d.tar.gz
gnunet-9aa22137e2427e1f44c7fca2f30005ba8b22195d.zip
postgres hackery
Diffstat (limited to 'src/datastore')
-rw-r--r--src/datastore/Makefile.am50
-rw-r--r--src/datastore/perf_plugin_datastore_data_postgres.conf33
-rw-r--r--src/datastore/plugin_datastore_postgres.c1190
-rw-r--r--src/datastore/test_datastore_api_data_postgres.conf55
4 files changed, 1326 insertions, 2 deletions
diff --git a/src/datastore/Makefile.am b/src/datastore/Makefile.am
index 3c687e8ce..55d972cd7 100644
--- a/src/datastore/Makefile.am
+++ b/src/datastore/Makefile.am
@@ -51,10 +51,19 @@ if HAVE_SQLITE
51 perf_datastore_api_sqlite \ 51 perf_datastore_api_sqlite \
52 perf_plugin_datastore_sqlite 52 perf_plugin_datastore_sqlite
53endif 53endif
54if HAVE_POSTGRES
55 POSTGRES_PLUGIN = libgnunet_plugin_datastore_postgres.la
56 POSTGRES_TESTS = \
57 test_datastore_api_postgres \
58 test_datastore_api_management_postgres \
59 perf_datastore_api_postgres \
60 perf_plugin_datastore_postgres
61endif
54 62
55plugin_LTLIBRARIES = \ 63plugin_LTLIBRARIES = \
56 $(SQLITE_PLUGIN) \ 64 $(SQLITE_PLUGIN) \
57 $(MYSQL_PLUGIN) \ 65 $(MYSQL_PLUGIN) \
66 $(POSTGRES_PLUGIN) \
58 libgnunet_plugin_datastore_template.la 67 libgnunet_plugin_datastore_template.la
59 68
60 69
@@ -77,6 +86,16 @@ libgnunet_plugin_datastore_mysql_la_LDFLAGS = \
77libgnunet_plugin_datastore_mysql_la_CPPFLAGS = \ 86libgnunet_plugin_datastore_mysql_la_CPPFLAGS = \
78 $(MYSQL_CPPFLAGS) 87 $(MYSQL_CPPFLAGS)
79 88
89libgnunet_plugin_datastore_postgres_la_SOURCES = \
90 plugin_datastore_postgres.c
91libgnunet_plugin_datastore_postgres_la_LIBADD = \
92 $(top_builddir)/src/statistics/libgnunetstatistics.la \
93 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) -lpq
94libgnunet_plugin_datastore_postgres_la_LDFLAGS = \
95 $(GN_PLUGIN_LDFLAGS) $(POSTGRES_LDFLAGS) -lpq
96libgnunet_plugin_datastore_postgres_la_CPPFLAGS = \
97 $(POSTGRES_CPPFLAGS)
98
80 99
81libgnunet_plugin_datastore_template_la_SOURCES = \ 100libgnunet_plugin_datastore_template_la_SOURCES = \
82 plugin_datastore_template.c 101 plugin_datastore_template.c
@@ -87,7 +106,8 @@ libgnunet_plugin_datastore_template_la_LDFLAGS = \
87 106
88check_PROGRAMS = \ 107check_PROGRAMS = \
89 $(SQLITE_TESTS) \ 108 $(SQLITE_TESTS) \
90 $(MYSQL_TESTS) 109 $(MYSQL_TESTS) \
110 $(POSTGRES_TESTS)
91 111
92if !DISABLE_TEST_RUN 112if !DISABLE_TEST_RUN
93TESTS = $(check_PROGRAMS) 113TESTS = $(check_PROGRAMS)
@@ -141,8 +161,34 @@ perf_plugin_datastore_mysql_LDADD = \
141 $(top_builddir)/src/util/libgnunetutil.la 161 $(top_builddir)/src/util/libgnunetutil.la
142 162
143 163
164test_datastore_api_postgres_SOURCES = \
165 test_datastore_api.c
166test_datastore_api_postgres_LDADD = \
167 $(top_builddir)/src/datastore/libgnunetdatastore.la \
168 $(top_builddir)/src/util/libgnunetutil.la
169
170test_datastore_api_management_postgres_SOURCES = \
171 test_datastore_api_management.c
172test_datastore_api_management_postgres_LDADD = \
173 $(top_builddir)/src/datastore/libgnunetdatastore.la \
174 $(top_builddir)/src/util/libgnunetutil.la
175
176perf_datastore_api_postgres_SOURCES = \
177 perf_datastore_api.c
178perf_datastore_api_postgres_LDADD = \
179 $(top_builddir)/src/datastore/libgnunetdatastore.la \
180 $(top_builddir)/src/util/libgnunetutil.la
181
182perf_plugin_datastore_postgres_SOURCES = \
183 perf_plugin_datastore.c
184perf_plugin_datastore_postgres_LDADD = \
185 $(top_builddir)/src/util/libgnunetutil.la
186
187
144EXTRA_DIST = \ 188EXTRA_DIST = \
145 test_datastore_api_data_sqlite.conf \ 189 test_datastore_api_data_sqlite.conf \
146 perf_plugin_datastore_data_sqlite.conf \ 190 perf_plugin_datastore_data_sqlite.conf \
147 test_datastore_api_data_mysql.conf \ 191 test_datastore_api_data_mysql.conf \
148 perf_plugin_datastore_data_mysql.conf 192 perf_plugin_datastore_data_mysql.conf \
193 test_datastore_api_data_postgres.conf \
194 perf_plugin_datastore_data_postgres.conf
diff --git a/src/datastore/perf_plugin_datastore_data_postgres.conf b/src/datastore/perf_plugin_datastore_data_postgres.conf
new file mode 100644
index 000000000..82ee17314
--- /dev/null
+++ b/src/datastore/perf_plugin_datastore_data_postgres.conf
@@ -0,0 +1,33 @@
1[PATHS]
2SERVICEHOME = /tmp/perf-gnunet-datastore-postgres/
3
4[datastore]
5PORT = 22654
6HOSTNAME = localhost
7HOME = $SERVICEHOME
8CONFIG = $DEFAULTCONFIG
9BINARY = gnunet-service-datastore
10ACCEPT_FROM = 127.0.0.1;
11ACCEPT_FROM6 = ::1;
12QUOTA = 1000000
13BLOOMFILTER = $SERVICEHOME/fs/bloomfilter
14DATABASE = postgres
15# USERNAME =
16# MAXBUF =
17# TIMEOUT =
18# DISABLEV6 =
19# BINDTO =
20# REJECT_FROM =
21# REJECT_FROM6 =
22# PREFIX =
23
24
25[dht]
26AUTOSTART = NO
27
28[datastore-postgres]
29CONFIG = dbname=gnunetcheck
30# USER =
31# PASSWORD =
32# HOST =
33# PORT =
diff --git a/src/datastore/plugin_datastore_postgres.c b/src/datastore/plugin_datastore_postgres.c
new file mode 100644
index 000000000..63a51d095
--- /dev/null
+++ b/src/datastore/plugin_datastore_postgres.c
@@ -0,0 +1,1190 @@
1/*
2 This file is part of GNUnet
3 (C) 2009, 2010 Christian Grothoff (and other contributing authors)
4
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
7 by the Free Software Foundation; either version 3, or (at your
8 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 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file datastore/plugin_datastore_postgres.c
23 * @brief postgres-based datastore backend
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "plugin_datastore.h"
29#include <postgresql/libpq-fe.h>
30
31#define DEBUG_POSTGRES GNUNET_NO
32
33#define SELECT_IT_LOW_PRIORITY "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
34 "WHERE (prio = $1 AND oid > $2) " \
35 "ORDER BY prio ASC,oid ASC LIMIT 1) "\
36 "UNION "\
37 "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
38 "WHERE (prio > $1 AND oid != $2)"\
39 "ORDER BY prio ASC,oid ASC LIMIT 1)"\
40 "ORDER BY prio ASC,oid ASC LIMIT 1"
41
42#define SELECT_IT_NON_ANONYMOUS "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
43 "WHERE (prio = $1 AND oid < $2)"\
44 " AND anonLevel=0 ORDER BY prio DESC,oid DESC LIMIT 1) "\
45 "UNION "\
46 "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
47 "WHERE (prio < $1 AND oid != $2)"\
48 " AND anonLevel=0 ORDER BY prio DESC,oid DESC LIMIT 1) "\
49 "ORDER BY prio DESC,oid DESC LIMIT 1"
50
51#define SELECT_IT_EXPIRATION_TIME "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
52 "WHERE (expire = $1 AND oid > $2) "\
53 "ORDER BY expire ASC,oid ASC LIMIT 1) "\
54 "UNION "\
55 "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
56 "WHERE (expire > $1 AND oid != $2) " \
57 "ORDER BY expire ASC,oid ASC LIMIT 1)"\
58 "ORDER BY expire ASC,oid ASC LIMIT 1"
59
60
61#define SELECT_IT_MIGRATION_ORDER "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
62 "WHERE (expire = $1 AND oid < $2)"\
63 " AND expire > $3 AND type!=3"\
64 " ORDER BY expire DESC,oid DESC LIMIT 1) "\
65 "UNION "\
66 "(SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "\
67 "WHERE (expire < $1 AND oid != $2)" \
68 " AND expire > $3 AND type!=3"\
69 " ORDER BY expire DESC,oid DESC LIMIT 1)"\
70 "ORDER BY expire DESC,oid DESC LIMIT 1"
71
72/**
73 * After how many ms "busy" should a DB operation fail for good?
74 * A low value makes sure that we are more responsive to requests
75 * (especially PUTs). A high value guarantees a higher success
76 * rate (SELECTs in iterate can take several seconds despite LIMIT=1).
77 *
78 * The default value of 1s should ensure that users do not experience
79 * huge latencies while at the same time allowing operations to succeed
80 * with reasonable probability.
81 */
82#define BUSY_TIMEOUT GNUNET_TIME_UNIT_SECONDS
83
84
85struct NextRequestClosure
86{
87 struct Plugin *plugin;
88 PluginIterator iter;
89 void *iter_cls;
90 const char *paramValues[5];
91 const char *pname;
92 int paramLengths[5];
93 int nparams; // nparams
94 struct GNUNET_TIME_Absolute now;
95 GNUNET_HashCode key;
96 GNUNET_HashCode vhash;
97 long long count;
98 long long off;
99 long long limit_off;
100 unsigned long long total;
101 unsigned long long last_expire;
102 unsigned int last_rowid; // last_rowid
103 unsigned int last_prio;
104 enum GNUNET_BLOCK_Type type;
105 int end_it;
106};
107
108
109/**
110 * Context for all functions in this plugin.
111 */
112struct Plugin
113{
114 /**
115 * Our execution environment.
116 */
117 struct GNUNET_DATASTORE_PluginEnvironment *env;
118
119 /**
120 * Native Postgres database handle.
121 */
122 PGconn *dbh;
123
124 /**
125 * Closure of the 'next_task' (must be freed if 'next_task' is cancelled).
126 */
127 struct NextRequestClosure *next_task_nc;
128
129 /**
130 * Pending task with scheduler for running the next request.
131 */
132 GNUNET_SCHEDULER_TaskIdentifier next_task;
133
134 unsigned long long payload;
135
136 unsigned int lastSync;
137
138};
139
140
141/**
142 * Check if the result obtained from Postgres has
143 * the desired status code. If not, log an error, clear the
144 * result and return GNUNET_SYSERR.
145 *
146 * @return GNUNET_OK if the result is acceptable
147 */
148static int
149check_result (struct Plugin *plugin,
150 PGresult * ret,
151 int expected_status,
152 const char *command, const char *args, int line)
153{
154 if (ret == NULL)
155 {
156 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
157 "datastore-postgres",
158 "Postgres failed to allocate result for `%s:%s' at %d\n",
159 command, args, line);
160 return GNUNET_SYSERR;
161 }
162 if (PQresultStatus (ret) != expected_status)
163 {
164 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
165 "datastore-postgres",
166 _("`%s:%s' failed at %s:%d with error: %s"),
167 command, args, __FILE__, line, PQerrorMessage (plugin->dbh));
168 PQclear (ret);
169 return GNUNET_SYSERR;
170 }
171 return GNUNET_OK;
172}
173
174/**
175 * Run simple SQL statement (without results).
176 */
177static int
178pq_exec (struct Plugin *plugin,
179 const char *sql, int line)
180{
181 PGresult *ret;
182 ret = PQexec (plugin->dbh, sql);
183 if (GNUNET_OK != check_result (plugin,
184 ret,
185 PGRES_COMMAND_OK, "PQexec", sql, line))
186 return GNUNET_SYSERR;
187 PQclear (ret);
188 return GNUNET_OK;
189}
190
191/**
192 * Prepare SQL statement.
193 */
194static int
195pq_prepare (struct Plugin *plugin,
196 const char *name, const char *sql, int nparms, int line)
197{
198 PGresult *ret;
199 ret = PQprepare (plugin->dbh, name, sql, nparms, NULL);
200 if (GNUNET_OK !=
201 check_result (plugin,
202 ret, PGRES_COMMAND_OK, "PQprepare", sql, line))
203 return GNUNET_SYSERR;
204 PQclear (ret);
205 return GNUNET_OK;
206}
207
208/**
209 * @brief Get a database handle
210 * @return GNUNET_OK on success, GNUNET_SYSERR on error
211 */
212static int
213init_connection (struct Plugin *plugin)
214{
215 char *conninfo;
216 PGresult *ret;
217
218 /* Open database and precompile statements */
219 conninfo = NULL;
220 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
221 "datastore-postgres",
222 "CONFIG",
223 &conninfo);
224 plugin->dbh = PQconnectdb (conninfo == NULL ? "" : conninfo);
225 GNUNET_free_non_null (conninfo);
226 if (NULL == plugin->dbh)
227 {
228 /* FIXME: warn about out-of-memory? */
229 return GNUNET_SYSERR;
230 }
231 if (PQstatus (plugin->dbh) != CONNECTION_OK)
232 {
233 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
234 "postgres",
235 _("Unable to initialize Postgres: %s"),
236 PQerrorMessage (plugin->dbh));
237 PQfinish (plugin->dbh);
238 plugin->dbh = NULL;
239 return GNUNET_SYSERR;
240 }
241 ret = PQexec (plugin->dbh,
242 "CREATE TABLE gn080 ("
243 " size INTEGER NOT NULL DEFAULT 0,"
244 " type INTEGER NOT NULL DEFAULT 0,"
245 " prio INTEGER NOT NULL DEFAULT 0,"
246 " anonLevel INTEGER NOT NULL DEFAULT 0,"
247 " expire BIGINT NOT NULL DEFAULT 0,"
248 " hash BYTEA NOT NULL DEFAULT '',"
249 " vhash BYTEA NOT NULL DEFAULT '',"
250 " value BYTEA NOT NULL DEFAULT '')" "WITH OIDS");
251 if ( (ret == NULL) ||
252 ( (PQresultStatus (ret) != PGRES_COMMAND_OK) &&
253 (0 != strcmp ("42P07", /* duplicate table */
254 PQresultErrorField
255 (ret,
256 PG_DIAG_SQLSTATE)))))
257 {
258 check_result (plugin,
259 ret, PGRES_COMMAND_OK, "CREATE TABLE", "gn080", __LINE__);
260 PQfinish (plugin->dbh);
261 plugin->dbh = NULL;
262 return GNUNET_SYSERR;
263 }
264 if (PQresultStatus (ret) == PGRES_COMMAND_OK)
265 {
266 if ((GNUNET_OK !=
267 pq_exec (plugin, "CREATE INDEX idx_hash ON gn080 (hash)", __LINE__)) ||
268 (GNUNET_OK !=
269 pq_exec (plugin, "CREATE INDEX idx_hash_vhash ON gn080 (hash,vhash)",
270 __LINE__))
271 || (GNUNET_OK !=
272 pq_exec (plugin, "CREATE INDEX idx_prio ON gn080 (prio)", __LINE__))
273 || (GNUNET_OK !=
274 pq_exec (plugin, "CREATE INDEX idx_expire ON gn080 (expire)", __LINE__))
275 || (GNUNET_OK !=
276 pq_exec (plugin, "CREATE INDEX idx_comb3 ON gn080 (prio,anonLevel)",
277 __LINE__))
278 || (GNUNET_OK !=
279 pq_exec
280 (plugin, "CREATE INDEX idx_comb4 ON gn080 (prio,hash,anonLevel)",
281 __LINE__))
282 || (GNUNET_OK !=
283 pq_exec (plugin, "CREATE INDEX idx_comb7 ON gn080 (expire,hash)",
284 __LINE__)))
285 {
286 PQclear (ret);
287 PQfinish (plugin->dbh);
288 plugin->dbh = NULL;
289 return GNUNET_SYSERR;
290 }
291 }
292 PQclear (ret);
293#if 1
294 ret = PQexec (plugin->dbh,
295 "ALTER TABLE gn080 ALTER value SET STORAGE EXTERNAL");
296 if ( (ret == NULL) ||
297 ((PQresultStatus (ret) != PGRES_COMMAND_OK) ) )
298 {
299 check_result (plugin,
300 ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn080", __LINE__);
301 PQfinish (plugin->dbh);
302 plugin->dbh = NULL;
303 return GNUNET_SYSERR;
304 }
305 ret = PQexec (plugin->dbh,
306 "ALTER TABLE gn080 ALTER hash SET STORAGE PLAIN");
307 if ( (ret == NULL) ||
308 ((PQresultStatus (ret) != PGRES_COMMAND_OK) ) )
309 {
310 check_result (plugin,
311 ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn080", __LINE__);
312 PQfinish (plugin->dbh);
313 plugin->dbh = NULL;
314 return GNUNET_SYSERR;
315 }
316 ret = PQexec (plugin->dbh,
317 "ALTER TABLE gn080 ALTER vhash SET STORAGE PLAIN");
318 if ( (ret == NULL) ||
319 ((PQresultStatus (ret) != PGRES_COMMAND_OK) ) )
320 {
321 check_result (plugin,
322 ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn080", __LINE__);
323 PQfinish (plugin->dbh);
324 plugin->dbh = NULL;
325 return GNUNET_SYSERR;
326 }
327#endif
328 if ((GNUNET_OK !=
329 pq_prepare (plugin,
330 "getvt",
331 "SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "
332 "WHERE hash=$1 AND vhash=$2 AND type=$3 "
333 "AND oid >= $4 ORDER BY oid ASC LIMIT 1 OFFSET $5",
334 5,
335 __LINE__)) ||
336 (GNUNET_OK !=
337 pq_prepare (plugin,
338 "gett",
339 "SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "
340 "WHERE hash=$1 AND type=$2"
341 "AND oid >= $3 ORDER BY oid ASC LIMIT 1 OFFSET $4",
342 4,
343 __LINE__)) ||
344 (GNUNET_OK !=
345 pq_prepare (plugin,
346 "getv",
347 "SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "
348 "WHERE hash=$1 AND vhash=$2"
349 "AND oid >= $3 ORDER BY oid ASC LIMIT 1 OFFSET $4",
350 4,
351 __LINE__)) ||
352 (GNUNET_OK !=
353 pq_prepare (plugin,
354 "get",
355 "SELECT size, type, prio, anonLevel, expire, hash, value, oid FROM gn080 "
356 "WHERE hash=$1"
357 "AND oid >= $2 ORDER BY oid ASC LIMIT 1 OFFSET $3",
358 3,
359 __LINE__)) ||
360 (GNUNET_OK !=
361 pq_prepare (plugin,
362 "put",
363 "INSERT INTO gn080 (size, type, prio, anonLevel, expire, hash, vhash, value) "
364 "VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
365 8,
366 __LINE__)) ||
367 (GNUNET_OK !=
368 pq_prepare (plugin,
369 "update",
370 "UPDATE gn080 SET prio = prio + $1, expire = CASE WHEN expire < $2 THEN $2 ELSE expire END "
371 "WHERE oid = $3",
372 3,
373 __LINE__)) ||
374 (GNUNET_OK !=
375 pq_prepare (plugin,
376 "select_low_priority",
377 SELECT_IT_LOW_PRIORITY,
378 2,
379 __LINE__)) ||
380 (GNUNET_OK !=
381 pq_prepare (plugin,
382 "select_non_anonymous",
383 SELECT_IT_NON_ANONYMOUS,
384 2,
385 __LINE__)) ||
386 (GNUNET_OK !=
387 pq_prepare (plugin,
388 "select_expiration_time",
389 SELECT_IT_EXPIRATION_TIME,
390 2,
391 __LINE__)) ||
392 (GNUNET_OK !=
393 pq_prepare (plugin,
394 "select_migration_order",
395 SELECT_IT_MIGRATION_ORDER,
396 3,
397 __LINE__)) ||
398 (GNUNET_OK !=
399 pq_prepare (plugin,
400 "delrow",
401 "DELETE FROM gn080 " "WHERE oid=$1", 1, __LINE__)))
402 {
403 PQfinish (plugin->dbh);
404 plugin->dbh = NULL;
405 return GNUNET_SYSERR;
406 }
407 return GNUNET_OK;
408}
409
410
411/**
412 * Delete the row identified by the given rowid (qid
413 * in postgres).
414 *
415 * @return GNUNET_OK on success
416 */
417static int
418delete_by_rowid (struct Plugin *plugin,
419 unsigned int rowid)
420{
421 const char *paramValues[] = { (const char *) &rowid };
422 int paramLengths[] = { sizeof (rowid) };
423 const int paramFormats[] = { 1 };
424 PGresult *ret;
425
426 ret = PQexecPrepared (plugin->dbh,
427 "delrow",
428 1, paramValues, paramLengths, paramFormats, 1);
429 if (GNUNET_OK !=
430 check_result (plugin,
431 ret, PGRES_COMMAND_OK, "PQexecPrepared", "delrow",
432 __LINE__))
433 {
434 return GNUNET_SYSERR;
435 }
436 PQclear (ret);
437 return GNUNET_OK;
438}
439
440
441/**
442 * Get an estimate of how much space the database is
443 * currently using.
444 *
445 * @param cls our "struct Plugin*"
446 * @return number of bytes used on disk
447 */
448static unsigned long long
449postgres_plugin_get_size (void *cls)
450{
451 struct Plugin *plugin = cls;
452 double ret;
453
454 ret = plugin->payload;
455 return (unsigned long long) (ret * 1.00);
456 /* benchmarking shows XX% overhead */
457}
458
459
460/**
461 * Store an item in the datastore.
462 *
463 * @param cls closure
464 * @param key key for the item
465 * @param size number of bytes in data
466 * @param data content stored
467 * @param type type of the content
468 * @param priority priority of the content
469 * @param anonymity anonymity-level for the content
470 * @param expiration expiration time for the content
471 * @param msg set to error message
472 * @return GNUNET_OK on success
473 */
474static int
475postgres_plugin_put (void *cls,
476 const GNUNET_HashCode * key,
477 uint32_t size,
478 const void *data,
479 enum GNUNET_BLOCK_Type type,
480 uint32_t priority,
481 uint32_t anonymity,
482 struct GNUNET_TIME_Absolute expiration,
483 char **msg)
484{
485 struct Plugin *plugin = cls;
486 GNUNET_HashCode vhash;
487 PGresult *ret;
488 const char *paramValues[] = {
489 (const char *) &size,
490 (const char *) &type,
491 (const char *) &priority,
492 (const char *) &anonymity,
493 (const char *) &expiration.value,
494 (const char *) key,
495 (const char *) &vhash,
496 (const char *) data
497 };
498 int paramLengths[] = {
499 sizeof (size),
500 sizeof (type),
501 sizeof (priority),
502 sizeof (anonymity),
503 sizeof (expiration.value),
504 sizeof (GNUNET_HashCode),
505 sizeof (GNUNET_HashCode),
506 size
507 };
508 const int paramFormats[] = { 1, 1, 1, 1, 1, 1, 1, 1 };
509
510 GNUNET_CRYPTO_hash (data, size, &vhash);
511 ret = PQexecPrepared (plugin->dbh,
512 "put", 8, paramValues, paramLengths, paramFormats, 1);
513 if (GNUNET_OK != check_result (plugin, ret,
514 PGRES_COMMAND_OK,
515 "PQexecPrepared", "put", __LINE__))
516 return GNUNET_SYSERR;
517 PQclear (ret);
518 plugin->payload += size;
519 return GNUNET_OK;
520}
521
522/**
523 * Function invoked on behalf of a "PluginIterator"
524 * asking the database plugin to call the iterator
525 * with the next item.
526 *
527 * @param cls the 'struct NextRequestClosure'
528 * @param tc scheduler context
529 */
530static void
531postgres_next_request_cont (void *next_cls,
532 const struct GNUNET_SCHEDULER_TaskContext *tc)
533{
534 struct NextRequestClosure *nrc = next_cls;
535 struct Plugin *plugin = nrc->plugin;
536 const int paramFormats[] = { 1, 1, 1, 1, 1 };
537 int iret;
538 PGresult *res;
539 enum GNUNET_BLOCK_Type type;
540 unsigned int anonymity;
541 unsigned int priority;
542 unsigned int size;
543 unsigned int rowid;
544 struct GNUNET_TIME_Absolute expiration_time;
545 GNUNET_HashCode key;
546
547 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
548 plugin->next_task_nc = NULL;
549 if (GNUNET_YES == nrc->end_it)
550 {
551 nrc->iter (nrc->iter_cls,
552 NULL, NULL, 0, NULL, 0, 0, 0,
553 GNUNET_TIME_UNIT_ZERO_ABS, 0);
554 GNUNET_free (nrc);
555 return;
556 }
557
558
559 if (nrc->count == 0)
560 nrc->limit_off = nrc->off;
561 else
562 nrc->limit_off = 0;
563
564 res = PQexecPrepared (plugin->dbh,
565 nrc->pname,
566 nrc->nparams,
567 nrc->paramValues,
568 nrc->paramLengths,
569 paramFormats, 1);
570 if (GNUNET_OK != check_result (plugin,
571 res,
572 PGRES_TUPLES_OK,
573 "PQexecPrepared",
574 nrc->pname,
575 __LINE__))
576 {
577 nrc->iter (nrc->iter_cls,
578 NULL, NULL, 0, NULL, 0, 0, 0,
579 GNUNET_TIME_UNIT_ZERO_ABS, 0);
580 GNUNET_free (nrc);
581 return;
582 }
583
584 if (0 == PQntuples (res))
585 {
586 /* no result */
587 nrc->iter (nrc->iter_cls,
588 NULL, NULL, 0, NULL, 0, 0, 0,
589 GNUNET_TIME_UNIT_ZERO_ABS, 0);
590 GNUNET_free (nrc);
591 return;
592 }
593 if ((1 != PQntuples (res)) ||
594 (8 != PQnfields (res)) ||
595 (sizeof (unsigned int) != PQfsize (res, 0)) ||
596 (sizeof (unsigned int) != PQfsize (res, 7)))
597 {
598 GNUNET_break (0);
599 nrc->iter (nrc->iter_cls,
600 NULL, NULL, 0, NULL, 0, 0, 0,
601 GNUNET_TIME_UNIT_ZERO_ABS, 0);
602 GNUNET_free (nrc);
603 return;
604 }
605 rowid = *(unsigned int *) PQgetvalue (res, 0, 7);
606 size = *(unsigned int *) PQgetvalue (res, 0, 0);
607 if ((sizeof (unsigned int) != PQfsize (res, 1)) ||
608 (sizeof (unsigned int) != PQfsize (res, 2)) ||
609 (sizeof (unsigned int) != PQfsize (res, 3)) ||
610 (sizeof (unsigned long long) != PQfsize (res, 4)) ||
611 (sizeof (GNUNET_HashCode) != PQgetlength (res, 0, 5)) ||
612 (size != PQgetlength (res, 0, 6)))
613 {
614 GNUNET_break (0);
615 delete_by_rowid (plugin, rowid);
616 nrc->iter (nrc->iter_cls,
617 NULL, NULL, 0, NULL, 0, 0, 0,
618 GNUNET_TIME_UNIT_ZERO_ABS, 0);
619 GNUNET_free (nrc);
620 return;
621 }
622
623 type = *(unsigned int *) PQgetvalue (res, 0, 1);
624 priority = *(unsigned int *) PQgetvalue (res, 0, 2);
625 anonymity = *(unsigned int *) PQgetvalue (res, 0, 3);
626 expiration_time.value = *(unsigned long long *) PQgetvalue (res, 0, 4);
627 size = PQgetlength (res, 0, 6);
628 memcpy (&key, PQgetvalue (res, 0, 5), sizeof (GNUNET_HashCode));
629
630 nrc->last_prio = priority;
631 nrc->last_expire = expiration_time.value;
632 nrc->last_rowid = rowid + 1;
633 nrc->count++;
634 iret = nrc->iter (nrc->iter_cls,
635 nrc,
636 &key,
637 size,
638 PQgetvalue (res, 0, 6),
639 (enum GNUNET_BLOCK_Type) type,
640 priority,
641 anonymity,
642 expiration_time,
643 rowid);
644 if (iret == GNUNET_SYSERR)
645 return;
646 if (iret == GNUNET_NO)
647 {
648 plugin->payload -= size;
649 delete_by_rowid (plugin, rowid);
650 }
651}
652
653
654
655/**
656 * Function invoked on behalf of a "PluginIterator"
657 * asking the database plugin to call the iterator
658 * with the next item.
659 *
660 * @param next_cls whatever argument was given
661 * to the PluginIterator as "next_cls".
662 * @param end_it set to GNUNET_YES if we
663 * should terminate the iteration early
664 * (iterator should be still called once more
665 * to signal the end of the iteration).
666 */
667static void
668postgres_plugin_next_request (void *next_cls,
669 int end_it)
670{
671 struct NextRequestClosure *nrc = next_cls;
672
673 if (GNUNET_YES == end_it)
674 nrc->end_it = GNUNET_YES;
675 nrc->plugin->next_task_nc = nrc;
676 nrc->plugin->next_task = GNUNET_SCHEDULER_add_now (nrc->plugin->env->sched,
677 &postgres_next_request_cont,
678 nrc);
679}
680
681
682/**
683 * Update the priority for a particular key in the datastore. If
684 * the expiration time in value is different than the time found in
685 * the datastore, the higher value should be kept. For the
686 * anonymity level, the lower value is to be used. The specified
687 * priority should be added to the existing priority, ignoring the
688 * priority in value.
689 *
690 * Note that it is possible for multiple values to match this put.
691 * In that case, all of the respective values are updated.
692 *
693 * @param cls our "struct Plugin*"
694 * @param uid unique identifier of the datum
695 * @param delta by how much should the priority
696 * change? If priority + delta < 0 the
697 * priority should be set to 0 (never go
698 * negative).
699 * @param expire new expiration time should be the
700 * MAX of any existing expiration time and
701 * this value
702 * @param msg set to error message
703 * @return GNUNET_OK on success
704 */
705static int
706postgres_plugin_update (void *cls,
707 uint64_t uid,
708 int delta, struct GNUNET_TIME_Absolute expire,
709 char **msg)
710{
711 struct Plugin *plugin = cls;
712 unsigned int oid = (unsigned int) uid; /* only 32 bit for postgres */
713 PGresult *ret;
714
715 const char *paramValues[] = {
716 (const char *) &delta,
717 (const char *) &expire.value,
718 (const char *) &oid,
719 };
720 int paramLengths[] = {
721 sizeof (delta),
722 sizeof (expire.value),
723 sizeof (oid),
724 };
725 const int paramFormats[] = { 1, 1, 1 };
726
727 ret = PQexecPrepared (plugin->dbh,
728 "update",
729 3, paramValues, paramLengths, paramFormats, 1);
730 if (GNUNET_OK != check_result (plugin,
731 ret,
732 PGRES_COMMAND_OK,
733 "PQexecPrepared", "update", __LINE__))
734 return GNUNET_SYSERR;
735 PQclear (ret);
736 return GNUNET_OK;
737}
738
739
740/**
741 * Call a method for each key in the database and
742 * call the callback method on it.
743 *
744 * @param type entries of which type should be considered?
745 * @param iter maybe NULL (to just count); iter
746 * should return GNUNET_SYSERR to abort the
747 * iteration, GNUNET_NO to delete the entry and
748 * continue and GNUNET_OK to continue iterating
749 */
750static void
751postgres_iterate (struct Plugin *plugin,
752 unsigned int type,
753 int is_asc,
754 unsigned int iter_select,
755 PluginIterator iter, void *iter_cls)
756{
757 struct NextRequestClosure *nrc;
758
759 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
760 nrc->plugin = plugin;
761 nrc->iter = iter;
762 nrc->iter_cls = iter_cls;
763 if (is_asc)
764 {
765 nrc->last_prio = 0;
766 nrc->last_rowid = 0;
767 nrc->last_expire = 0;
768 }
769 else
770 {
771 nrc->last_prio = 0x7FFFFFFFL;
772 nrc->last_rowid = 0xFFFFFFFF;
773 nrc->last_expire = 0x7FFFFFFFFFFFFFFFLL;
774 }
775 switch (iter_select)
776 {
777 case 0:
778 nrc->pname = "select_low_priority";
779 nrc->nparams = 2;
780 nrc->paramValues[0] = (const char *) &nrc->last_prio;
781 nrc->paramValues[1] = (const char *) &nrc->last_rowid;
782 nrc->paramLengths[0] = sizeof (nrc->last_prio);
783 nrc->paramLengths[1] = sizeof (nrc->last_rowid);
784 break;
785 case 1:
786 nrc->pname = "select_non_anonymous";
787 nrc->nparams = 2;
788 nrc->paramValues[0] = (const char *) &nrc->last_prio;
789 nrc->paramValues[1] = (const char *) &nrc->last_rowid;
790 nrc->paramLengths[0] = sizeof (nrc->last_prio);
791 nrc->paramLengths[1] = sizeof (nrc->last_rowid);
792 break;
793 case 2:
794 nrc->pname = "select_expiration_time";
795 nrc->nparams = 2;
796 nrc->paramValues[0] = (const char *) &nrc->last_expire;
797 nrc->paramValues[1] = (const char *) &nrc->last_rowid;
798 nrc->paramLengths[0] = sizeof (nrc->last_expire);
799 nrc->paramLengths[1] = sizeof (nrc->last_rowid);
800 break;
801 case 3:
802 nrc->pname = "select_migration_order";
803 nrc->nparams = 3;
804 nrc->paramValues[0] = (const char *) &nrc->last_expire;
805 nrc->paramValues[1] = (const char *) &nrc->last_rowid;
806 nrc->paramValues[2] = (const char *) &nrc->now;
807 nrc->paramLengths[0] = sizeof (nrc->last_expire);
808 nrc->paramLengths[1] = sizeof (nrc->last_rowid);
809 nrc->paramLengths[2] = sizeof (nrc->now);
810 break;
811 default:
812 GNUNET_break (0);
813 iter (iter_cls,
814 NULL, NULL, 0, NULL, 0, 0, 0,
815 GNUNET_TIME_UNIT_ZERO_ABS, 0);
816 return;
817 }
818 nrc->now = GNUNET_TIME_absolute_get ();
819 postgres_plugin_next_request (nrc,
820 GNUNET_NO);
821}
822
823
824/**
825 * Select a subset of the items in the datastore and call
826 * the given iterator for each of them.
827 *
828 * @param cls our "struct Plugin*"
829 * @param type entries of which type should be considered?
830 * Use 0 for any type.
831 * @param iter function to call on each matching value;
832 * will be called once with a NULL value at the end
833 * @param iter_cls closure for iter
834 */
835static void
836postgres_plugin_iter_low_priority (void *cls,
837 enum GNUNET_BLOCK_Type type,
838 PluginIterator iter,
839 void *iter_cls)
840{
841 struct Plugin *plugin = cls;
842
843 postgres_iterate (plugin,
844 type,
845 GNUNET_YES, 0,
846 iter, iter_cls);
847}
848
849
850
851
852/**
853 * Iterate over the results for a particular key
854 * in the datastore.
855 *
856 * @param cls closure
857 * @param key maybe NULL (to match all entries)
858 * @param vhash hash of the value, maybe NULL (to
859 * match all values that have the right key).
860 * Note that for DBlocks there is no difference
861 * betwen key and vhash, but for other blocks
862 * there may be!
863 * @param type entries of which type are relevant?
864 * Use 0 for any type.
865 * @param iter function to call on each matching value;
866 * will be called once with a NULL value at the end
867 * @param iter_cls closure for iter
868 */
869static void
870postgres_plugin_get (void *cls,
871 const GNUNET_HashCode * key,
872 const GNUNET_HashCode * vhash,
873 enum GNUNET_BLOCK_Type type,
874 PluginIterator iter, void *iter_cls)
875{
876 struct Plugin *plugin = cls;
877 struct NextRequestClosure *nrc;
878 const int paramFormats[] = { 1, 1, 1, 1, 1 };
879 PGresult *ret;
880
881 if (key == NULL)
882 {
883 postgres_plugin_iter_low_priority (plugin, type,
884 iter, iter_cls);
885 return;
886 }
887 nrc = GNUNET_malloc (sizeof (struct NextRequestClosure));
888 nrc->plugin = plugin;
889 nrc->iter = iter;
890 nrc->iter_cls = iter_cls;
891 nrc->key = *key;
892 if (vhash != NULL)
893 nrc->vhash = *vhash;
894 nrc->paramValues[0] = (const char*) &nrc->key;
895 nrc->paramLengths[0] = sizeof (GNUNET_HashCode);
896 nrc->type = type;
897 if (type != 0)
898 {
899 if (vhash != NULL)
900 {
901 nrc->paramValues[1] = (const char *) &nrc->vhash;
902 nrc->paramLengths[1] = sizeof (nrc->vhash);
903 nrc->paramValues[2] = (const char *) &nrc->type;
904 nrc->paramLengths[2] = sizeof (nrc->type);
905 nrc->paramValues[3] = (const char *) &nrc->last_rowid;
906 nrc->paramLengths[3] = sizeof (nrc->last_rowid);
907 nrc->paramValues[4] = (const char *) &nrc->limit_off;
908 nrc->paramLengths[4] = sizeof (nrc->limit_off);
909 nrc->nparams = 5;
910 nrc->pname = "getvt";
911 ret = PQexecParams (plugin->dbh,
912 "SELECT count(*) FROM gn080 WHERE hash=$1 AND vhash=$2 AND type=$3",
913 3,
914 NULL,
915 nrc->paramValues,
916 nrc->paramLengths,
917 paramFormats, 1);
918 }
919 else
920 {
921 nrc->paramValues[1] = (const char *) &nrc->type;
922 nrc->paramLengths[1] = sizeof (nrc->type);
923 nrc->paramValues[2] = (const char *) &nrc->last_rowid;
924 nrc->paramLengths[2] = sizeof (nrc->last_rowid);
925 nrc->paramValues[3] = (const char *) &nrc->limit_off;
926 nrc->paramLengths[3] = sizeof (nrc->limit_off);
927 nrc->nparams = 4;
928 nrc->pname = "gett";
929 ret = PQexecParams (plugin->dbh,
930 "SELECT count(*) FROM gn080 WHERE hash=$1 AND type=$2",
931 2,
932 NULL,
933 nrc->paramValues,
934 nrc->paramLengths,
935 paramFormats, 1);
936 }
937 }
938 else
939 {
940 if (vhash != NULL)
941 {
942 nrc->paramValues[1] = (const char *) &nrc->vhash;
943 nrc->paramLengths[1] = sizeof (nrc->vhash);
944 nrc->paramValues[2] = (const char *) &nrc->last_rowid;
945 nrc->paramLengths[2] = sizeof (nrc->last_rowid);
946 nrc->paramValues[3] = (const char *) &nrc->limit_off;
947 nrc->paramLengths[3] = sizeof (nrc->limit_off);
948 nrc->nparams = 4;
949 nrc->pname = "getv";
950 ret = PQexecParams (plugin->dbh,
951 "SELECT count(*) FROM gn080 WHERE hash=$1 AND vhash=$2",
952 2,
953 NULL,
954 nrc->paramValues,
955 nrc->paramLengths,
956 paramFormats, 1);
957 }
958 else
959 {
960 nrc->paramValues[1] = (const char *) &nrc->last_rowid;
961 nrc->paramLengths[1] = sizeof (nrc->last_rowid);
962 nrc->paramValues[2] = (const char *) &nrc->limit_off;
963 nrc->paramLengths[2] = sizeof (nrc->limit_off);
964 nrc->nparams = 3;
965 nrc->pname = "get";
966 ret = PQexecParams (plugin->dbh,
967 "SELECT count(*) FROM gn080 WHERE hash=$1",
968 1,
969 NULL,
970 nrc->paramValues,
971 nrc->paramLengths,
972 paramFormats, 1);
973 }
974 }
975 if (GNUNET_OK != check_result (plugin,
976 ret,
977 PGRES_TUPLES_OK,
978 "PQexecParams",
979 nrc->pname,
980 __LINE__))
981 {
982 iter (iter_cls,
983 NULL, NULL, 0, NULL, 0, 0, 0,
984 GNUNET_TIME_UNIT_ZERO_ABS, 0);
985 return;
986 }
987 if ((PQntuples (ret) != 1) ||
988 (PQnfields (ret) != 1) ||
989 (PQgetlength (ret, 0, 0) != sizeof (unsigned long long)))
990 {
991 GNUNET_break (0);
992 PQclear (ret);
993 iter (iter_cls,
994 NULL, NULL, 0, NULL, 0, 0, 0,
995 GNUNET_TIME_UNIT_ZERO_ABS, 0);
996 return;
997 }
998 nrc->total = GNUNET_ntohll (*(const unsigned long long *) PQgetvalue (ret, 0, 0));
999 PQclear (ret);
1000 if (nrc->total == 0)
1001 {
1002 iter (iter_cls,
1003 NULL, NULL, 0, NULL, 0, 0, 0,
1004 GNUNET_TIME_UNIT_ZERO_ABS, 0);
1005 return;
1006 }
1007 nrc->off = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1008 nrc->total);
1009 postgres_plugin_next_request (nrc,
1010 GNUNET_NO);
1011}
1012
1013
1014/**
1015 * Select a subset of the items in the datastore and call
1016 * the given iterator for each of them.
1017 *
1018 * @param cls our "struct Plugin*"
1019 * @param type entries of which type should be considered?
1020 * Use 0 for any type.
1021 * @param iter function to call on each matching value;
1022 * will be called once with a NULL value at the end
1023 * @param iter_cls closure for iter
1024 */
1025static void
1026postgres_plugin_iter_zero_anonymity (void *cls,
1027 enum GNUNET_BLOCK_Type type,
1028 PluginIterator iter,
1029 void *iter_cls)
1030{
1031 struct Plugin *plugin = cls;
1032
1033 postgres_iterate (plugin,
1034 type, GNUNET_NO, 1,
1035 iter, iter_cls);
1036}
1037
1038
1039/**
1040 * Select a subset of the items in the datastore and call
1041 * the given iterator for each of them.
1042 *
1043 * @param cls our "struct Plugin*"
1044 * @param type entries of which type should be considered?
1045 * Use 0 for any type.
1046 * @param iter function to call on each matching value;
1047 * will be called once with a NULL value at the end
1048 * @param iter_cls closure for iter
1049 */
1050static void
1051postgres_plugin_iter_ascending_expiration (void *cls,
1052 enum GNUNET_BLOCK_Type type,
1053 PluginIterator iter,
1054 void *iter_cls)
1055{
1056 struct Plugin *plugin = cls;
1057
1058 postgres_iterate (plugin, type, GNUNET_YES, 2,
1059 iter, iter_cls);
1060}
1061
1062
1063
1064/**
1065 * Select a subset of the items in the datastore and call
1066 * the given iterator for each of them.
1067 *
1068 * @param cls our "struct Plugin*"
1069 * @param type entries of which type should be considered?
1070 * Use 0 for any type.
1071 * @param iter function to call on each matching value;
1072 * will be called once with a NULL value at the end
1073 * @param iter_cls closure for iter
1074 */
1075static void
1076postgres_plugin_iter_migration_order (void *cls,
1077 enum GNUNET_BLOCK_Type type,
1078 PluginIterator iter,
1079 void *iter_cls)
1080{
1081 struct Plugin *plugin = cls;
1082
1083 postgres_iterate (plugin, 0, GNUNET_NO, 3,
1084 iter, iter_cls);
1085}
1086
1087
1088
1089/**
1090 * Select a subset of the items in the datastore and call
1091 * the given iterator for each of them.
1092 *
1093 * @param cls our "struct Plugin*"
1094 * @param type entries of which type should be considered?
1095 * Use 0 for any type.
1096 * @param iter function to call on each matching value;
1097 * will be called once with a NULL value at the end
1098 * @param iter_cls closure for iter
1099 */
1100static void
1101postgres_plugin_iter_all_now (void *cls,
1102 enum GNUNET_BLOCK_Type type,
1103 PluginIterator iter,
1104 void *iter_cls)
1105{
1106 struct Plugin *plugin = cls;
1107
1108 postgres_iterate (plugin,
1109 0, GNUNET_YES, 0,
1110 iter, iter_cls);
1111}
1112
1113
1114/**
1115 * Drop database.
1116 */
1117static void
1118postgres_plugin_drop (void *cls)
1119{
1120 struct Plugin *plugin = cls;
1121
1122 pq_exec (plugin, "DROP TABLE gn080", __LINE__);
1123}
1124
1125
1126/**
1127 * Entry point for the plugin.
1128 *
1129 * @param cls the "struct GNUNET_DATASTORE_PluginEnvironment*"
1130 * @return our "struct Plugin*"
1131 */
1132void *
1133libgnunet_plugin_datastore_postgres_init (void *cls)
1134{
1135 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1136 struct GNUNET_DATASTORE_PluginFunctions *api;
1137 struct Plugin *plugin;
1138
1139 plugin = GNUNET_malloc (sizeof (struct Plugin));
1140 plugin->env = env;
1141 if (GNUNET_OK != init_connection (plugin))
1142 {
1143 GNUNET_free (plugin);
1144 return NULL;
1145 }
1146 api = GNUNET_malloc (sizeof (struct GNUNET_DATASTORE_PluginFunctions));
1147 api->cls = plugin;
1148 api->get_size = &postgres_plugin_get_size;
1149 api->put = &postgres_plugin_put;
1150 api->next_request = &postgres_plugin_next_request;
1151 api->get = &postgres_plugin_get;
1152 api->update = &postgres_plugin_update;
1153 api->iter_low_priority = &postgres_plugin_iter_low_priority;
1154 api->iter_zero_anonymity = &postgres_plugin_iter_zero_anonymity;
1155 api->iter_ascending_expiration = &postgres_plugin_iter_ascending_expiration;
1156 api->iter_migration_order = &postgres_plugin_iter_migration_order;
1157 api->iter_all_now = &postgres_plugin_iter_all_now;
1158 api->drop = &postgres_plugin_drop;
1159 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1160 "postgres", _("Postgres database running\n"));
1161 return api;
1162}
1163
1164
1165/**
1166 * Exit point from the plugin.
1167 * @param cls our "struct Plugin*"
1168 * @return always NULL
1169 */
1170void *
1171libgnunet_plugin_datastore_postgres_done (void *cls)
1172{
1173 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1174 struct Plugin *plugin = api->cls;
1175
1176 if (plugin->next_task != GNUNET_SCHEDULER_NO_TASK)
1177 {
1178 GNUNET_SCHEDULER_cancel (plugin->env->sched,
1179 plugin->next_task);
1180 plugin->next_task = GNUNET_SCHEDULER_NO_TASK;
1181 GNUNET_free (plugin->next_task_nc);
1182 plugin->next_task_nc = NULL;
1183 }
1184 PQfinish (plugin->dbh);
1185 GNUNET_free (plugin);
1186 GNUNET_free (api);
1187 return NULL;
1188}
1189
1190/* end of plugin_datastore_postgres.c */
diff --git a/src/datastore/test_datastore_api_data_postgres.conf b/src/datastore/test_datastore_api_data_postgres.conf
new file mode 100644
index 000000000..23eb02f97
--- /dev/null
+++ b/src/datastore/test_datastore_api_data_postgres.conf
@@ -0,0 +1,55 @@
1[PATHS]
2SERVICEHOME = /tmp/test-gnunet-datastore-postgres/
3
4[arm]
5DEFAULTSERVICES =
6PORT = 42466
7HOSTNAME = localhost
8
9[resolver]
10PORT = 42464
11HOSTNAME = localhost
12
13[datastore]
14PORT = 22654
15HOSTNAME = localhost
16HOME = $SERVICEHOME
17CONFIG = $DEFAULTCONFIG
18BINARY = gnunet-service-datastore
19ACCEPT_FROM = 127.0.0.1;
20ACCEPT_FROM6 = ::1;
21QUOTA = 10000000
22BLOOMFILTER = $SERVICEHOME/fs/bloomfilter
23DATABASE = postgres
24DISABLE_SOCKET_FORWARDING = YES
25# USERNAME =
26# MAXBUF =
27# TIMEOUT =
28# DISABLEV6 =
29# BINDTO =
30# REJECT_FROM =
31# REJECT_FROM6 =
32# PREFIX =
33# DEBUG = YES
34# PREFIX = xterm -T datastore -e gdb --args
35PREFIX = valgrind --tool=memcheck --leak-check=yes
36BINARY = gnunet-service-datastore
37
38[datastore-postgres]
39CONFIG = dbname=gnunetcheck
40# USER =
41# PASSWORD =
42# HOST =
43# PORT =
44
45[statistics]
46PORT = 22667
47HOSTNAME = localhost
48
49[testing]
50WEAKRANDOM = YES
51
52[dht]
53AUTOSTART = NO
54
55