2 This file is part of GNUnet
3 (C) 2006, 2009, 2010 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file datacache/plugin_datacache_postgres.c
23 * @brief postgres for an implementation of a database backend for the datacache
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "plugin_datacache.h"
29 #include <postgresql/libpq-fe.h>
31 #define DEBUG_POSTGRES GNUNET_NO
34 * Per-entry overhead estimate
36 #define OVERHEAD (sizeof(GNUNET_HashCode) + 24)
39 * Context for all functions in this plugin.
44 * Our execution environment.
46 struct GNUNET_DATACACHE_PluginEnvironment *env;
49 * Native Postgres database handle.
57 * Check if the result obtained from Postgres has
58 * the desired status code. If not, log an error, clear the
59 * result and return GNUNET_SYSERR.
61 * @return GNUNET_OK if the result is acceptable
64 check_result (struct Plugin *plugin,
67 const char *command, const char *args, int line)
71 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
73 "Postgres failed to allocate result for `%s:%s' at %d\n",
77 if (PQresultStatus (ret) != expected_status)
79 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
81 _("`%s:%s' failed at %s:%d with error: %s"),
82 command, args, __FILE__, line, PQerrorMessage (plugin->dbh));
91 * Run simple SQL statement (without results).
94 pq_exec (struct Plugin *plugin,
95 const char *sql, int line)
98 ret = PQexec (plugin->dbh, sql);
99 if (GNUNET_OK != check_result (plugin,
101 PGRES_COMMAND_OK, "PQexec", sql, line))
102 return GNUNET_SYSERR;
109 * Prepare SQL statement.
112 pq_prepare (struct Plugin *plugin,
113 const char *name, const char *sql, int nparms, int line)
116 ret = PQprepare (plugin->dbh, name, sql, nparms, NULL);
118 check_result (plugin,
119 ret, PGRES_COMMAND_OK, "PQprepare", sql, line))
120 return GNUNET_SYSERR;
127 * @brief Get a database handle
128 * @return GNUNET_OK on success, GNUNET_SYSERR on error
131 init_connection (struct Plugin *plugin)
136 /* Open database and precompile statements */
138 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
139 "datacache-postgres",
142 plugin->dbh = PQconnectdb (conninfo == NULL ? "" : conninfo);
143 GNUNET_free_non_null (conninfo);
144 if (NULL == plugin->dbh)
146 /* FIXME: warn about out-of-memory? */
147 return GNUNET_SYSERR;
149 if (PQstatus (plugin->dbh) != CONNECTION_OK)
151 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
152 "datacache-postgres",
153 _("Unable to initialize Postgres: %s"),
154 PQerrorMessage (plugin->dbh));
155 PQfinish (plugin->dbh);
157 return GNUNET_SYSERR;
159 ret = PQexec (plugin->dbh,
160 "CREATE TEMPORARY TABLE gn090dc ("
161 " type INTEGER NOT NULL DEFAULT 0,"
162 " discard_time BIGINT NOT NULL DEFAULT 0,"
163 " key BYTEA NOT NULL DEFAULT '',"
164 " value BYTEA NOT NULL DEFAULT '')" "WITH OIDS");
165 if ( (ret == NULL) ||
166 ( (PQresultStatus (ret) != PGRES_COMMAND_OK) &&
167 (0 != strcmp ("42P07", /* duplicate table */
170 PG_DIAG_SQLSTATE)))))
172 check_result (plugin,
173 ret, PGRES_COMMAND_OK, "CREATE TABLE", "gn090dc", __LINE__);
174 PQfinish (plugin->dbh);
176 return GNUNET_SYSERR;
178 if (PQresultStatus (ret) == PGRES_COMMAND_OK)
181 pq_exec (plugin, "CREATE INDEX idx_key ON gn090dc (key)", __LINE__)) ||
183 pq_exec (plugin, "CREATE INDEX idx_dt ON gn090dc (discard_time)",
187 PQfinish (plugin->dbh);
189 return GNUNET_SYSERR;
194 ret = PQexec (plugin->dbh,
195 "ALTER TABLE gn090dc ALTER value SET STORAGE EXTERNAL");
197 check_result (plugin,
198 ret, PGRES_COMMAND_OK,
199 "ALTER TABLE", "gn090dc", __LINE__))
201 PQfinish (plugin->dbh);
203 return GNUNET_SYSERR;
206 ret = PQexec (plugin->dbh,
207 "ALTER TABLE gn090dc ALTER key SET STORAGE PLAIN");
209 check_result (plugin,
210 ret, PGRES_COMMAND_OK,
211 "ALTER TABLE", "gn090dc", __LINE__))
213 PQfinish (plugin->dbh);
215 return GNUNET_SYSERR;
222 "SELECT discard_time,type,value FROM gn090dc "
223 "WHERE key=$1 AND type=$2 ",
229 "SELECT discard_time,type,value FROM gn090dc "
236 "SELECT length(value),oid,key FROM gn090dc "
237 "ORDER BY discard_time ASC LIMIT 1",
243 "DELETE FROM gn090dc WHERE oid=$1",
249 "INSERT INTO gn090dc (type, discard_time, key, value) "
250 "VALUES ($1, $2, $3, $4)",
254 PQfinish (plugin->dbh);
256 return GNUNET_SYSERR;
263 * Delete the row identified by the given rowid (qid
266 * @return GNUNET_OK on success
269 delete_by_rowid (struct Plugin *plugin,
272 const char *paramValues[] = { (const char *) &rowid };
273 int paramLengths[] = { sizeof (rowid) };
274 const int paramFormats[] = { 1 };
277 ret = PQexecPrepared (plugin->dbh,
279 1, paramValues, paramLengths, paramFormats, 1);
281 check_result (plugin,
282 ret, PGRES_COMMAND_OK, "PQexecPrepared", "delrow",
285 return GNUNET_SYSERR;
293 * Store an item in the datastore.
295 * @param cls closure (our "struct Plugin")
296 * @param key key to store data under
297 * @param size number of bytes in data
298 * @param data data to store
299 * @param type type of the value
300 * @param discard_time when to discard the value in any case
301 * @return 0 on error, number of bytes used otherwise
304 postgres_plugin_put (void *cls,
305 const GNUNET_HashCode * key,
308 enum GNUNET_BLOCK_Type type,
309 struct GNUNET_TIME_Absolute discard_time)
311 struct Plugin *plugin = cls;
313 uint32_t btype = htonl (type);
314 uint64_t bexpi = GNUNET_TIME_absolute_hton (discard_time).value__;
315 const char *paramValues[] = {
316 (const char *) &btype,
317 (const char *) &bexpi,
321 int paramLengths[] = {
324 sizeof (GNUNET_HashCode),
327 const int paramFormats[] = { 1, 1, 1, 1 };
329 ret = PQexecPrepared (plugin->dbh,
330 "put", 4, paramValues, paramLengths, paramFormats, 1);
331 if (GNUNET_OK != check_result (plugin, ret,
333 "PQexecPrepared", "put", __LINE__))
334 return GNUNET_SYSERR;
336 return size + OVERHEAD;
341 * Iterate over the results for a particular key
344 * @param cls closure (our "struct Plugin")
346 * @param type entries of which type are relevant?
347 * @param iter maybe NULL (to just count)
348 * @param iter_cls closure for iter
349 * @return the number of results found
352 postgres_plugin_get (void *cls,
353 const GNUNET_HashCode * key,
354 enum GNUNET_BLOCK_Type type,
355 GNUNET_DATACACHE_Iterator iter,
358 struct Plugin *plugin = cls;
359 uint32_t btype = htonl (type);
360 const char *paramValues[] = {
362 (const char *) &btype,
364 int paramLengths[] = {
365 sizeof (GNUNET_HashCode),
368 const int paramFormats[] = { 1, 1 };
369 struct GNUNET_TIME_Absolute expiration_time;
376 res = PQexecPrepared (plugin->dbh,
377 (type == 0) ? "getk" : "getkt",
383 if (GNUNET_OK != check_result (plugin,
387 (type == 0) ? "getk" : "getkt",
391 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
392 "datacache-postgres",
393 "Ending iteration (postgres error)\n");
398 if (0 == (cnt = PQntuples (res)))
402 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
403 "datacache-postgres",
404 "Ending iteration (no more results)\n");
411 if ( (3 != PQnfields (res)) ||
412 (sizeof (uint64_t) != PQfsize (res, 0)) ||
413 (sizeof (uint32_t) != PQfsize (res, 1)))
421 expiration_time.value = GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, i, 0));
422 type = ntohl (*(uint32_t *) PQgetvalue (res, i, 1));
423 size = PQgetlength (res, i, 2);
425 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
426 "datacache-postgres",
427 "Found result of size %u bytes and type %u in database\n",
429 (unsigned int) type);
436 PQgetvalue (res, i, 2),
437 (enum GNUNET_BLOCK_Type) type))
440 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
441 "datacache-postgres",
442 "Ending iteration (client error)\n");
454 * Delete the entry with the lowest expiration value
455 * from the datacache right now.
457 * @param cls closure (our "struct Plugin")
458 * @return GNUNET_OK on success, GNUNET_SYSERR on error
461 postgres_plugin_del (void *cls)
463 struct Plugin *plugin = cls;
469 res = PQexecPrepared (plugin->dbh,
473 if (GNUNET_OK != check_result (plugin,
481 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
482 "datacache-postgres",
483 "Ending iteration (postgres error)\n");
487 if (0 == PQntuples (res))
491 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
492 "datacache-postgres",
493 "Ending iteration (no more results)\n");
496 return GNUNET_SYSERR;
498 if ( (3 != PQnfields (res)) ||
499 (sizeof (size) != PQfsize (res, 0)) ||
500 (sizeof (oid) != PQfsize (res, 1)) ||
501 (sizeof (GNUNET_HashCode) != PQgetlength (res, 0, 2)) )
507 size = ntohl (*(uint32_t *) PQgetvalue (res, 0, 0));
508 oid = ntohl (*(uint32_t *) PQgetvalue (res, 0, 1));
510 PQgetvalue (res, 0, 2),
511 sizeof (GNUNET_HashCode));
513 if (GNUNET_OK != delete_by_rowid (plugin, oid))
514 return GNUNET_SYSERR;
515 plugin->env->delete_notify (plugin->env->cls,
523 * Entry point for the plugin.
525 * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet")
526 * @return the plugin's closure (our "struct Plugin")
529 libgnunet_plugin_datacache_postgres_init (void *cls)
531 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
532 struct GNUNET_DATACACHE_PluginFunctions *api;
533 struct Plugin *plugin;
535 plugin = GNUNET_malloc (sizeof (struct Plugin));
539 init_connection (plugin))
541 GNUNET_free (plugin);
545 api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions));
547 api->get = &postgres_plugin_get;
548 api->put = &postgres_plugin_put;
549 api->del = &postgres_plugin_del;
550 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
551 "datacache-postgres",
552 _("Postgres datacache running\n"));
558 * Exit point from the plugin.
560 * @param cls closure (our "struct Plugin")
564 libgnunet_plugin_datacache_postgres_done (void *cls)
566 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
567 struct Plugin *plugin = api->cls;
570 "Unloading postgres plugin\n");
571 PQfinish (plugin->dbh);
572 GNUNET_free (plugin);
579 /* end of plugin_datacache_postgres.c */