aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac4
-rw-r--r--contrib/defaults.conf3
-rw-r--r--doc/README.mysql95
-rw-r--r--doc/README.postgres49
-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
8 files changed, 1475 insertions, 4 deletions
diff --git a/configure.ac b/configure.ac
index d7912fcc7..9817a4f34 100644
--- a/configure.ac
+++ b/configure.ac
@@ -330,8 +330,8 @@ AC_ARG_WITH(postgres,
330 CPPFLAGS="-I$with_postgres/include $CPPFLAGS" 330 CPPFLAGS="-I$with_postgres/include $CPPFLAGS"
331 AC_CHECK_HEADERS(postgresql/libpq-fe.h, 331 AC_CHECK_HEADERS(postgresql/libpq-fe.h,
332 EXT_LIB_PATH="-L$with_postgres/lib $EXT_LIB_PATH" 332 EXT_LIB_PATH="-L$with_postgres/lib $EXT_LIB_PATH"
333 SQLITE_LDFLAGS="-L$with_postgres/lib" 333 POSTGRES_LDFLAGS="-L$with_postgres/lib"
334 SQLITE_CPPFLAGS="-I$with_postgres/include" 334 POSTGRES_CPPFLAGS="-I$with_postgres/include"
335 postgres=true) 335 postgres=true)
336 LDFLAGS=$SAVE_LDFLAGS 336 LDFLAGS=$SAVE_LDFLAGS
337 CPPFLAGS=$SAVE_CPPFLAGS 337 CPPFLAGS=$SAVE_CPPFLAGS
diff --git a/contrib/defaults.conf b/contrib/defaults.conf
index d9f3114cd..73f303b3f 100644
--- a/contrib/defaults.conf
+++ b/contrib/defaults.conf
@@ -200,6 +200,9 @@ DATABASE = sqlite
200[datastore-sqlite] 200[datastore-sqlite]
201FILENAME = $SERVICEHOME/datastore/sqlite.db 201FILENAME = $SERVICEHOME/datastore/sqlite.db
202 202
203[datastore-postgres]
204CONFIG = connect_timeout=10;dbname=gnunet
205
203[datastore-mysql] 206[datastore-mysql]
204DATABASE = gnunet 207DATABASE = gnunet
205# CONFIG = ~/.my.cnf 208# CONFIG = ~/.my.cnf
diff --git a/doc/README.mysql b/doc/README.mysql
new file mode 100644
index 000000000..e123c43ac
--- /dev/null
+++ b/doc/README.mysql
@@ -0,0 +1,95 @@
1How to setup the MySQL database for GNUnet.
2
3NOTE: This db module does NOT work with mysql before 4.1 since we need
4prepared statements. We are generally testing the code against MySQL
55.0 at this point.
6
7HIGHLIGHTS
8
9Pros
10 + On up-to-date hardware where mysql can be used comfortably, this
11 module will have better performance than the other db choices
12 (according to our tests).
13 + Its often possible to recover the mysql database from internal
14 inconsistencies. The other db choices do not support repair
15 (gnunet-check cannot fix problems internal to the dbmgr!).
16 For example, we have seen several cases where power failure
17 has ruined a gdbm database beyond repair.
18 + much faster (for one of the key benchmarks -- content migration
19 -- we have measure mysql taking 2s for an operation where
20 sqlite takes 150s).
21Cons
22 - Memory usage (Comment: "I have 1G and it never caused me trouble")
23 - Manual setup
24
25MANUAL SETUP INSTRUCTIONS
26
27 1) in /etc/gnunet.conf, set
28 sqstore = "sqstore_mysql"
29
30 2) Then access mysql as root,
31 $ mysql -u root -p
32 and do the following. [You should replace $USER with the username
33 that will be running the gnunetd process].
34
35 CREATE DATABASE gnunet;
36 GRANT select,insert,update,delete,create,alter,drop,create temporary tables
37 ON gnunet.* TO $USER@localhost;
38 SET PASSWORD FOR $USER@localhost=PASSWORD('$the_password_you_like');
39 FLUSH PRIVILEGES;
40
41 3) In the $HOME directory of $USER, create a ".my.cnf" file
42 with the following lines
43
44 [client]
45 user=$USER
46 password=$the_password_you_like
47
48 Thats it. Note that .my.cnf file is a security risk unless its on
49 a safe partition etc. The $HOME/.my.cnf can of course be a symbolic
50 link. Even greater security risk can be achieved by setting no
51 password for $USER. Luckily $USER has only priviledges to mess
52 up GNUnet's tables, nothing else (unless you give him more,
53 of course).
54
55 4) Still, perhaps you should briefly try if the DB connection
56 works. First, login as $USER. Then use,
57
58 $ mysql -u $USER
59 mysql> use gnunet;
60
61 If you get the message "Database changed" it probably works.
62
63 [If you get "ERROR 2002: Can't connect to local MySQL server
64 through socket '/tmp/mysql.sock' (2)" it may be resolvable by
65 "ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock"
66 so there may be some additional trouble depending on your mysql setup.]
67
68 5) If you want to run the testcases, you must create a second
69 database "gnunetcheck" with the same username and password.
70 This database will then be used for testing ("make check").
71
72
73REPAIRING TABLES
74
75- Its probably healthy to check your tables for inconsistencies
76 every now and then, especially after system crashes.
77- If you get odd SEGVs on gnunetd startup, it might be that the mysql
78 databases have been corrupted.
79- The tables can be verified/fixed in two ways;
80 1) by shutting down mysqld (mandatory!) and running
81 # myisamchk -r *.MYI
82 in /var/lib/mysql/gnunet/ (or wherever the tables are stored).
83 Another repair command is "mysqlcheck". The usable command
84 may depend on your mysql build/version. Or,
85 2) by executing
86 mysql> REPAIR TABLE gn090;
87
88
89PROBLEMS?
90
91If you have problems related to the mysql module, your best friend is
92probably the mysql manual. The first thing to check is that mysql is
93basically operational, that you can connect to it, create tables,
94issue queries etc.
95
diff --git a/doc/README.postgres b/doc/README.postgres
new file mode 100644
index 000000000..0213c72fb
--- /dev/null
+++ b/doc/README.postgres
@@ -0,0 +1,49 @@
1How to setup the Postgres database for GNUnet.
2
3NOTE: This db module was developed for Postgres 8.3. I have no
4idea what the minimum version that we require is exactly.
5
6HIGHLIGHTS
7
8Pros
9 + Easier to setup than MySQL
10 + Real database
11Cons
12 - Quite slow
13 - Still some setup
14
15MANUAL SETUP INSTRUCTIONS
16
17 1) in /etc/gnunet.conf, set
18 sqstore = "sqstore_postgres"
19
20 2) Then access postgres to create a user; I had to do this to get
21 access and create a user:
22 # su - postgres
23 $ createuser
24 At this point, use the name of the user running gnunet
25 for the role, do not set it to superuser, allow the creation
26 of databases.
27
28 3) As that user, create a database (or two):
29 $ createdb gnunet
30 $ createdb gnunetcheck # this way you can run "make check"
31
32 Thats it.
33
34 4) Still, perhaps you should briefly try if the DB connection
35 works. First, login as the user who will run gnunetd. Then use,
36
37 $ psql gnunet # or gnunetcheck
38 gnunet=> \dt
39
40 If, after you have started gnunetd at least once, you get a
41 gn090 table here, it probably works.
42
43PROBLEMS?
44
45If you have problems related to the postgres module, your best friend
46is probably the postgres manual. The first thing to check is that
47postgres is basically operational, that you can connect to it, create
48tables, issue queries etc. (see step 4 above for details).
49
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