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 uint32_t brow = htonl (rowid);
273 const char *paramValues[] = { (const char *) &brow };
274 int paramLengths[] = { sizeof (brow) };
275 const int paramFormats[] = { 1 };
278 ret = PQexecPrepared (plugin->dbh,
280 1, paramValues, paramLengths, paramFormats, 1);
282 check_result (plugin,
283 ret, PGRES_COMMAND_OK, "PQexecPrepared", "delrow",
286 return GNUNET_SYSERR;
294 * Store an item in the datastore.
296 * @param cls closure (our "struct Plugin")
297 * @param key key to store data under
298 * @param size number of bytes in data
299 * @param data data to store
300 * @param type type of the value
301 * @param discard_time when to discard the value in any case
302 * @return 0 on error, number of bytes used otherwise
305 postgres_plugin_put (void *cls,
306 const GNUNET_HashCode * key,
309 enum GNUNET_BLOCK_Type type,
310 struct GNUNET_TIME_Absolute discard_time)
312 struct Plugin *plugin = cls;
314 uint32_t btype = htonl (type);
315 uint64_t bexpi = GNUNET_TIME_absolute_hton (discard_time).value__;
316 const char *paramValues[] = {
317 (const char *) &btype,
318 (const char *) &bexpi,
322 int paramLengths[] = {
325 sizeof (GNUNET_HashCode),
328 const int paramFormats[] = { 1, 1, 1, 1 };
330 ret = PQexecPrepared (plugin->dbh,
331 "put", 4, paramValues, paramLengths, paramFormats, 1);
332 if (GNUNET_OK != check_result (plugin, ret,
334 "PQexecPrepared", "put", __LINE__))
335 return GNUNET_SYSERR;
337 return size + OVERHEAD;
342 * Iterate over the results for a particular key
345 * @param cls closure (our "struct Plugin")
347 * @param type entries of which type are relevant?
348 * @param iter maybe NULL (to just count)
349 * @param iter_cls closure for iter
350 * @return the number of results found
353 postgres_plugin_get (void *cls,
354 const GNUNET_HashCode * key,
355 enum GNUNET_BLOCK_Type type,
356 GNUNET_DATACACHE_Iterator iter,
359 struct Plugin *plugin = cls;
360 uint32_t btype = htonl (type);
361 const char *paramValues[] = {
363 (const char *) &btype,
365 int paramLengths[] = {
366 sizeof (GNUNET_HashCode),
369 const int paramFormats[] = { 1, 1 };
370 struct GNUNET_TIME_Absolute expiration_time;
377 res = PQexecPrepared (plugin->dbh,
378 (type == 0) ? "getk" : "getkt",
384 if (GNUNET_OK != check_result (plugin,
388 (type == 0) ? "getk" : "getkt",
392 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
393 "datacache-postgres",
394 "Ending iteration (postgres error)\n");
399 if (0 == (cnt = PQntuples (res)))
403 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
404 "datacache-postgres",
405 "Ending iteration (no more results)\n");
415 if ( (3 != PQnfields (res)) ||
416 (sizeof (uint64_t) != PQfsize (res, 0)) ||
417 (sizeof (uint32_t) != PQfsize (res, 1)))
425 expiration_time.value = GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, i, 0));
426 type = ntohl (*(uint32_t *) PQgetvalue (res, i, 1));
427 size = PQgetlength (res, i, 2);
429 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
430 "datacache-postgres",
431 "Found result of size %u bytes and type %u in database\n",
433 (unsigned int) type);
440 PQgetvalue (res, i, 2),
441 (enum GNUNET_BLOCK_Type) type))
444 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
445 "datacache-postgres",
446 "Ending iteration (client error)\n");
458 * Delete the entry with the lowest expiration value
459 * from the datacache right now.
461 * @param cls closure (our "struct Plugin")
462 * @return GNUNET_OK on success, GNUNET_SYSERR on error
465 postgres_plugin_del (void *cls)
467 struct Plugin *plugin = cls;
473 res = PQexecPrepared (plugin->dbh,
477 if (GNUNET_OK != check_result (plugin,
485 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
486 "datacache-postgres",
487 "Ending iteration (postgres error)\n");
491 if (0 == PQntuples (res))
495 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
496 "datacache-postgres",
497 "Ending iteration (no more results)\n");
500 return GNUNET_SYSERR;
502 if ( (3 != PQnfields (res)) ||
503 (sizeof (size) != PQfsize (res, 0)) ||
504 (sizeof (oid) != PQfsize (res, 1)) ||
505 (sizeof (GNUNET_HashCode) != PQgetlength (res, 0, 2)) )
511 size = ntohl (*(uint32_t *) PQgetvalue (res, 0, 0));
512 oid = ntohl (*(uint32_t *) PQgetvalue (res, 0, 1));
514 PQgetvalue (res, 0, 2),
515 sizeof (GNUNET_HashCode));
517 if (GNUNET_OK != delete_by_rowid (plugin, oid))
518 return GNUNET_SYSERR;
519 plugin->env->delete_notify (plugin->env->cls,
527 * Entry point for the plugin.
529 * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet")
530 * @return the plugin's closure (our "struct Plugin")
533 libgnunet_plugin_datacache_postgres_init (void *cls)
535 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
536 struct GNUNET_DATACACHE_PluginFunctions *api;
537 struct Plugin *plugin;
539 plugin = GNUNET_malloc (sizeof (struct Plugin));
543 init_connection (plugin))
545 GNUNET_free (plugin);
549 api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions));
551 api->get = &postgres_plugin_get;
552 api->put = &postgres_plugin_put;
553 api->del = &postgres_plugin_del;
554 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
555 "datacache-postgres",
556 _("Postgres datacache running\n"));
562 * Exit point from the plugin.
564 * @param cls closure (our "struct Plugin")
568 libgnunet_plugin_datacache_postgres_done (void *cls)
570 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
571 struct Plugin *plugin = api->cls;
573 PQfinish (plugin->dbh);
574 GNUNET_free (plugin);
581 /* end of plugin_datacache_postgres.c */