aboutsummaryrefslogtreecommitdiff
path: root/src/datacache
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2010-08-26 13:32:21 +0000
committerChristian Grothoff <christian@grothoff.org>2010-08-26 13:32:21 +0000
commitd90d66f22e4b3f227067a8f06cc7e0e994a38c2c (patch)
tree45737f029aa36bcdfec60f93ef9cc085a5a1f97a /src/datacache
parentdeed215d0d135d30beac950a5f88050497ef9e39 (diff)
downloadgnunet-d90d66f22e4b3f227067a8f06cc7e0e994a38c2c.tar.gz
gnunet-d90d66f22e4b3f227067a8f06cc7e0e994a38c2c.zip
towards datacache
Diffstat (limited to 'src/datacache')
-rw-r--r--src/datacache/plugin_datacache_postgres.c519
1 files changed, 519 insertions, 0 deletions
diff --git a/src/datacache/plugin_datacache_postgres.c b/src/datacache/plugin_datacache_postgres.c
new file mode 100644
index 000000000..5959d54a6
--- /dev/null
+++ b/src/datacache/plugin_datacache_postgres.c
@@ -0,0 +1,519 @@
1/*
2 This file is part of GNUnet
3 (C) 2006, 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 datacache/plugin_datacache_postgres.c
23 * @brief postgres for an implementation of a database backend for the datacache
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "plugin_datacache.h"
29#include <postgresql/libpq-fe.h>
30
31#define DEBUG_POSTGRES GNUNET_NO
32
33
34
35/**
36 * Context for all functions in this plugin.
37 */
38struct Plugin
39{
40 /**
41 * Our execution environment.
42 */
43 struct GNUNET_DATACACHE_PluginEnvironment *env;
44
45 /**
46 * Native Postgres database handle.
47 */
48 PGconn *dbh;
49
50};
51
52
53/**
54 * Check if the result obtained from Postgres has
55 * the desired status code. If not, log an error, clear the
56 * result and return GNUNET_SYSERR.
57 *
58 * @return GNUNET_OK if the result is acceptable
59 */
60static int
61check_result (struct Plugin *plugin,
62 PGresult * ret,
63 int expected_status,
64 const char *command, const char *args, int line)
65{
66 if (ret == NULL)
67 {
68 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
69 "datastore-postgres",
70 "Postgres failed to allocate result for `%s:%s' at %d\n",
71 command, args, line);
72 return GNUNET_SYSERR;
73 }
74 if (PQresultStatus (ret) != expected_status)
75 {
76 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
77 "datastore-postgres",
78 _("`%s:%s' failed at %s:%d with error: %s"),
79 command, args, __FILE__, line, PQerrorMessage (plugin->dbh));
80 PQclear (ret);
81 return GNUNET_SYSERR;
82 }
83 return GNUNET_OK;
84}
85
86
87/**
88 * Run simple SQL statement (without results).
89 */
90static int
91pq_exec (struct Plugin *plugin,
92 const char *sql, int line)
93{
94 PGresult *ret;
95 ret = PQexec (plugin->dbh, sql);
96 if (GNUNET_OK != check_result (plugin,
97 ret,
98 PGRES_COMMAND_OK, "PQexec", sql, line))
99 return GNUNET_SYSERR;
100 PQclear (ret);
101 return GNUNET_OK;
102}
103
104
105/**
106 * Prepare SQL statement.
107 */
108static int
109pq_prepare (struct Plugin *plugin,
110 const char *name, const char *sql, int nparms, int line)
111{
112 PGresult *ret;
113 ret = PQprepare (plugin->dbh, name, sql, nparms, NULL);
114 if (GNUNET_OK !=
115 check_result (plugin,
116 ret, PGRES_COMMAND_OK, "PQprepare", sql, line))
117 return GNUNET_SYSERR;
118 PQclear (ret);
119 return GNUNET_OK;
120}
121
122
123/**
124 * @brief Get a database handle
125 * @return GNUNET_OK on success, GNUNET_SYSERR on error
126 */
127static int
128init_connection (struct Plugin *plugin)
129{
130 char *conninfo;
131 PGresult *ret;
132
133 /* Open database and precompile statements */
134 conninfo = NULL;
135 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
136 "datacache-postgres",
137 "CONFIG",
138 &conninfo);
139 plugin->dbh = PQconnectdb (conninfo == NULL ? "" : conninfo);
140 GNUNET_free_non_null (conninfo);
141 if (NULL == plugin->dbh)
142 {
143 /* FIXME: warn about out-of-memory? */
144 return GNUNET_SYSERR;
145 }
146 if (PQstatus (plugin->dbh) != CONNECTION_OK)
147 {
148 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
149 "datacache-postgres",
150 _("Unable to initialize Postgres: %s"),
151 PQerrorMessage (plugin->dbh));
152 PQfinish (plugin->dbh);
153 plugin->dbh = NULL;
154 return GNUNET_SYSERR;
155 }
156 ret = PQexec (plugin->dbh,
157 "CREATE TEMPORARY `TABLE gn090dc ("
158 " type INTEGER NOT NULL DEFAULT 0,"
159 " discard_time BIGINT NOT NULL DEFAULT 0,"
160 " key BYTEA NOT NULL DEFAULT '',"
161 " value BYTEA NOT NULL DEFAULT '')" "WITH OIDS");
162 if ( (ret == NULL) ||
163 ( (PQresultStatus (ret) != PGRES_COMMAND_OK) &&
164 (0 != strcmp ("42P07", /* duplicate table */
165 PQresultErrorField
166 (ret,
167 PG_DIAG_SQLSTATE)))))
168 {
169 check_result (plugin,
170 ret, PGRES_COMMAND_OK, "CREATE TABLE", "gn090dc", __LINE__);
171 PQfinish (plugin->dbh);
172 plugin->dbh = NULL;
173 return GNUNET_SYSERR;
174 }
175 if (PQresultStatus (ret) == PGRES_COMMAND_OK)
176 {
177 if ((GNUNET_OK !=
178 pq_exec (plugin, "CREATE INDEX idx_key ON gn090dc (key)", __LINE__)) ||
179 (GNUNET_OK !=
180 pq_exec (plugin, "CREATE INDEX idx_dt ON gn090 (discard_time)",
181 __LINE__)) )
182 {
183 PQclear (ret);
184 PQfinish (plugin->dbh);
185 plugin->dbh = NULL;
186 return GNUNET_SYSERR;
187 }
188 }
189 PQclear (ret);
190#if 1
191 ret = PQexec (plugin->dbh,
192 "ALTER TABLE gn090dc ALTER value SET STORAGE EXTERNAL");
193 if (GNUNET_OK !=
194 check_result (plugin,
195 ret, PGRES_COMMAND_OK,
196 "ALTER TABLE", "gn090dc", __LINE__))
197 {
198 PQfinish (plugin->dbh);
199 plugin->dbh = NULL;
200 return GNUNET_SYSERR;
201 }
202 PQclear (ret);
203 ret = PQexec (plugin->dbh,
204 "ALTER TABLE gn090dc ALTER key SET STORAGE PLAIN");
205 if (GNUNET_OK !=
206 check_result (plugin,
207 ret, PGRES_COMMAND_OK,
208 "ALTER TABLE", "gn090dc", __LINE__))
209 {
210 PQfinish (plugin->dbh);
211 plugin->dbh = NULL;
212 return GNUNET_SYSERR;
213 }
214 PQclear (ret);
215#endif
216 if ((GNUNET_OK !=
217 pq_prepare (plugin,
218 "getkt",
219 "SELECT discard_time,type,value FROM gn090dc "
220 "WHERE hash=$1 type=$2 ",
221 2,
222 __LINE__)) ||
223 (GNUNET_OK !=
224 pq_prepare (plugin,
225 "getk",
226 "SELECT discard_time,type,value FROM gn090dc "
227 "WHERE hash=$1",
228 1,
229 __LINE__)) ||
230 (GNUNET_OK !=
231 pq_prepare (plugin,
232 "getm",
233 "SELECT length(value),oid FROM gn090dc"
234 "ORDER BY discard_time ASC LIMIT 1",
235 0,
236 __LINE__)) ||
237 (GNUNET_OK !=
238 pq_prepare (plugin,
239 "delrow",
240 "DELETE FROM gn090dc WHERE oid=$1",
241 1,
242 __LINE__)) ||
243 (GNUNET_OK !=
244 pq_prepare (plugin,
245 "put",
246 "INSERT INTO gn090dc (type, discard_time, key, value) "
247 "VALUES ($1, $2, $3, $4)",
248 4,
249 __LINE__)) )
250 {
251 PQfinish (plugin->dbh);
252 plugin->dbh = NULL;
253 return GNUNET_SYSERR;
254 }
255 return GNUNET_OK;
256}
257
258
259/**
260 * Delete the row identified by the given rowid (qid
261 * in postgres).
262 *
263 * @return GNUNET_OK on success
264 */
265static int
266delete_by_rowid (struct Plugin *plugin,
267 unsigned int rowid)
268{
269 const char *paramValues[] = { (const char *) &rowid };
270 int paramLengths[] = { sizeof (rowid) };
271 const int paramFormats[] = { 1 };
272 PGresult *ret;
273
274 ret = PQexecPrepared (plugin->dbh,
275 "delrow",
276 1, paramValues, paramLengths, paramFormats, 1);
277 if (GNUNET_OK !=
278 check_result (plugin,
279 ret, PGRES_COMMAND_OK, "PQexecPrepared", "delrow",
280 __LINE__))
281 {
282 return GNUNET_SYSERR;
283 }
284 PQclear (ret);
285 return GNUNET_OK;
286}
287
288
289/**
290 * Store an item in the datastore.
291 *
292 * @param cls closure (our "struct Plugin")
293 * @param key key to store data under
294 * @param size number of bytes in data
295 * @param data data to store
296 * @param type type of the value
297 * @param discard_time when to discard the value in any case
298 * @return 0 on error, number of bytes used otherwise
299 */
300static uint32_t
301postgres_plugin_put (void *cls,
302 const GNUNET_HashCode * key,
303 uint32_t size,
304 const char *data,
305 enum GNUNET_BLOCK_Type type,
306 struct GNUNET_TIME_Absolute discard_time)
307{
308 struct Plugin *plugin = cls;
309 PGresult *ret;
310 uint32_t btype = htonl (type);
311 uint64_t bexpi = GNUNET_TIME_absolute_hton (discard_time).value__;
312 const char *paramValues[] = {
313 (const char *) &btype,
314 (const char *) &bexpi,
315 (const char *) key,
316 (const char *) data
317 };
318 int paramLengths[] = {
319 sizeof (btype),
320 sizeof (bexpi),
321 sizeof (GNUNET_HashCode),
322 size
323 };
324 const int paramFormats[] = { 1, 1, 1, 1 };
325
326 ret = PQexecPrepared (plugin->dbh,
327 "put", 4, paramValues, paramLengths, paramFormats, 1);
328 if (GNUNET_OK != check_result (plugin, ret,
329 PGRES_COMMAND_OK,
330 "PQexecPrepared", "put", __LINE__))
331 return GNUNET_SYSERR;
332 PQclear (ret);
333 return size;
334}
335
336
337/**
338 * Iterate over the results for a particular key
339 * in the datastore.
340 *
341 * @param cls closure (our "struct Plugin")
342 * @param key
343 * @param type entries of which type are relevant?
344 * @param iter maybe NULL (to just count)
345 * @param iter_cls closure for iter
346 * @return the number of results found
347 */
348static unsigned int
349postgres_plugin_get (void *cls,
350 const GNUNET_HashCode * key,
351 enum GNUNET_BLOCK_Type type,
352 GNUNET_DATACACHE_Iterator iter,
353 void *iter_cls)
354{
355 struct Plugin *plugin = cls;
356 uint32_t btype = htonl (type);
357 const char *paramValues[] = {
358 (const char *) key,
359 (const char *) &btype,
360 };
361 int paramLengths[] = {
362 sizeof (GNUNET_HashCode),
363 sizeof (btype),
364 };
365 const int paramFormats[] = { 1, 1 };
366 struct GNUNET_TIME_Absolute expiration_time;
367 uint32_t size;
368 unsigned int cnt;
369 unsigned int i;
370 PGresult *res;
371
372 cnt = 0;
373 res = PQexecPrepared (plugin->dbh,
374 (type == 0) ? "getk" : "getkt",
375 (type == 0) ? 1 : 2,
376 paramValues,
377 paramLengths,
378 paramFormats,
379 1);
380 if (GNUNET_OK != check_result (plugin,
381 res,
382 PGRES_TUPLES_OK,
383 "PQexecPrepared",
384 (type == 0) ? "getk" : "getkt",
385 __LINE__))
386 {
387#if DEBUG_POSTGRES
388 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
389 "datacache-postgres",
390 "Ending iteration (postgres error)\n");
391#endif
392 return 0;
393 }
394
395 if (0 == (cnt = PQntuples (res)))
396 {
397 /* no result */
398#if DEBUG_POSTGRES
399 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
400 "datacache-postgres",
401 "Ending iteration (no more results)\n");
402#endif
403 PQclear (res);
404 return 0;
405 }
406 if ( (3 != PQnfields (res)) ||
407 (sizeof (uint64_t) != PQfsize (res, 0)) ||
408 (sizeof (uint32_t) != PQfsize (res, 1)))
409 {
410 GNUNET_break (0);
411 PQclear (res);
412 return 0;
413 }
414 for (i=0;i<cnt;i++)
415 {
416 expiration_time.value = GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, i, 0));
417 type = ntohl (*(uint32_t *) PQgetvalue (res, i, 1));
418 size = PQgetlength (res, i, 2);
419#if DEBUG_POSTGRES
420 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
421 "datacache-postgres",
422 "Found result of size %u bytes and type %u in database\n",
423 (unsigned int) size,
424 (unsigned int) type);
425#endif
426 if (GNUNET_SYSERR ==
427 iter (iter_cls,
428 expiration_time,
429 key,
430 size,
431 PQgetvalue (res, i, 2),
432 (enum GNUNET_BLOCK_Type) type))
433 {
434#if DEBUG_POSTGRES
435 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
436 "datacache-postgres",
437 "Ending iteration (client error)\n");
438#endif
439 PQclear (res);
440 return cnt;
441 }
442 }
443 PQclear (res);
444 return cnt;
445}
446
447
448/**
449 * Delete the entry with the lowest expiration value
450 * from the datacache right now.
451 *
452 * @param cls closure (our "struct Plugin")
453 * @return GNUNET_OK on success, GNUNET_SYSERR on error
454 */
455static int
456postgres_plugin_del (void *cls)
457{
458
459 GNUNET_break (0);
460 return GNUNET_SYSERR;
461}
462
463
464/**
465 * Entry point for the plugin.
466 *
467 * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet")
468 * @return the plugin's closure (our "struct Plugin")
469 */
470void *
471libgnunet_plugin_datacache_postgres_init (void *cls)
472{
473 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
474 struct GNUNET_DATACACHE_PluginFunctions *api;
475 struct Plugin *plugin;
476
477 plugin = GNUNET_malloc (sizeof (struct Plugin));
478 plugin->env = env;
479
480 if (GNUNET_OK !=
481 init_connection (plugin))
482 {
483 GNUNET_free (plugin);
484 return NULL;
485 }
486
487 api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions));
488 api->cls = plugin;
489 api->get = &postgres_plugin_get;
490 api->put = &postgres_plugin_put;
491 api->del = &postgres_plugin_del;
492 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
493 "datacache-postgres",
494 _("Postgres datacache running\n"));
495 return api;
496}
497
498
499/**
500 * Exit point from the plugin.
501 *
502 * @param cls closure (our "struct Plugin")
503 * @return NULL
504 */
505void *
506libgnunet_plugin_datacache_postgres_done (void *cls)
507{
508 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
509 struct Plugin *plugin = api->cls;
510
511 GNUNET_free (plugin);
512 GNUNET_free (api);
513 return NULL;
514}
515
516
517
518/* end of plugin_datacache_postgres.c */
519