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 "gnunet_datacache_plugin.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",
143 plugin->dbh = PQconnectdb (conninfo == NULL ? "" : conninfo);
144 GNUNET_free_non_null (conninfo);
145 if (NULL == plugin->dbh)
147 /* FIXME: warn about out-of-memory? */
148 return GNUNET_SYSERR;
150 if (PQstatus (plugin->dbh) != CONNECTION_OK)
152 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
153 "datacache-postgres",
154 _("Unable to initialize Postgres: %s"),
155 PQerrorMessage (plugin->dbh));
156 PQfinish (plugin->dbh);
158 return GNUNET_SYSERR;
160 ret = PQexec (plugin->dbh,
161 "CREATE TEMPORARY TABLE gn090dc ("
162 " type INTEGER NOT NULL DEFAULT 0,"
163 " discard_time BIGINT NOT NULL DEFAULT 0,"
164 " key BYTEA NOT NULL DEFAULT '',"
165 " value BYTEA NOT NULL DEFAULT '')" "WITH OIDS");
166 if ( (ret == NULL) ||
167 ( (PQresultStatus (ret) != PGRES_COMMAND_OK) &&
168 (0 != strcmp ("42P07", /* duplicate table */
171 PG_DIAG_SQLSTATE)))))
173 (void) check_result (plugin,
174 ret, PGRES_COMMAND_OK, "CREATE TABLE", "gn090dc", __LINE__);
175 PQfinish (plugin->dbh);
177 return GNUNET_SYSERR;
179 if (PQresultStatus (ret) == PGRES_COMMAND_OK)
182 pq_exec (plugin, "CREATE INDEX idx_key ON gn090dc (key)", __LINE__)) ||
184 pq_exec (plugin, "CREATE INDEX idx_dt ON gn090dc (discard_time)",
188 PQfinish (plugin->dbh);
190 return GNUNET_SYSERR;
195 ret = PQexec (plugin->dbh,
196 "ALTER TABLE gn090dc ALTER value SET STORAGE EXTERNAL");
198 check_result (plugin,
199 ret, PGRES_COMMAND_OK,
200 "ALTER TABLE", "gn090dc", __LINE__))
202 PQfinish (plugin->dbh);
204 return GNUNET_SYSERR;
207 ret = PQexec (plugin->dbh,
208 "ALTER TABLE gn090dc ALTER key SET STORAGE PLAIN");
210 check_result (plugin,
211 ret, PGRES_COMMAND_OK,
212 "ALTER TABLE", "gn090dc", __LINE__))
214 PQfinish (plugin->dbh);
216 return GNUNET_SYSERR;
223 "SELECT discard_time,type,value FROM gn090dc "
224 "WHERE key=$1 AND type=$2 ",
230 "SELECT discard_time,type,value FROM gn090dc "
237 "SELECT length(value),oid,key FROM gn090dc "
238 "ORDER BY discard_time ASC LIMIT 1",
244 "DELETE FROM gn090dc WHERE oid=$1",
250 "INSERT INTO gn090dc (type, discard_time, key, value) "
251 "VALUES ($1, $2, $3, $4)",
255 PQfinish (plugin->dbh);
257 return GNUNET_SYSERR;
264 * Delete the row identified by the given rowid (qid
267 * @return GNUNET_OK on success
270 delete_by_rowid (struct Plugin *plugin,
273 uint32_t brow = htonl (rowid);
274 const char *paramValues[] = { (const char *) &brow };
275 int paramLengths[] = { sizeof (brow) };
276 const int paramFormats[] = { 1 };
279 ret = PQexecPrepared (plugin->dbh,
281 1, paramValues, paramLengths, paramFormats, 1);
283 check_result (plugin,
284 ret, PGRES_COMMAND_OK, "PQexecPrepared", "delrow",
287 return GNUNET_SYSERR;
295 * Store an item in the datastore.
297 * @param cls closure (our "struct Plugin")
298 * @param key key to store data under
299 * @param size number of bytes in data
300 * @param data data to store
301 * @param type type of the value
302 * @param discard_time when to discard the value in any case
303 * @return 0 on error, number of bytes used otherwise
306 postgres_plugin_put (void *cls,
307 const GNUNET_HashCode * key,
310 enum GNUNET_BLOCK_Type type,
311 struct GNUNET_TIME_Absolute discard_time)
313 struct Plugin *plugin = cls;
315 uint32_t btype = htonl (type);
316 uint64_t bexpi = GNUNET_TIME_absolute_hton (discard_time).abs_value__;
317 const char *paramValues[] = {
318 (const char *) &btype,
319 (const char *) &bexpi,
323 int paramLengths[] = {
326 sizeof (GNUNET_HashCode),
329 const int paramFormats[] = { 1, 1, 1, 1 };
331 ret = PQexecPrepared (plugin->dbh,
332 "put", 4, paramValues, paramLengths, paramFormats, 1);
333 if (GNUNET_OK != check_result (plugin, ret,
335 "PQexecPrepared", "put", __LINE__))
336 return GNUNET_SYSERR;
338 return size + OVERHEAD;
343 * Iterate over the results for a particular key
346 * @param cls closure (our "struct Plugin")
348 * @param type entries of which type are relevant?
349 * @param iter maybe NULL (to just count)
350 * @param iter_cls closure for iter
351 * @return the number of results found
354 postgres_plugin_get (void *cls,
355 const GNUNET_HashCode * key,
356 enum GNUNET_BLOCK_Type type,
357 GNUNET_DATACACHE_Iterator iter,
360 struct Plugin *plugin = cls;
361 uint32_t btype = htonl (type);
362 const char *paramValues[] = {
364 (const char *) &btype,
366 int paramLengths[] = {
367 sizeof (GNUNET_HashCode),
370 const int paramFormats[] = { 1, 1 };
371 struct GNUNET_TIME_Absolute expiration_time;
378 res = PQexecPrepared (plugin->dbh,
379 (type == 0) ? "getk" : "getkt",
385 if (GNUNET_OK != check_result (plugin,
389 (type == 0) ? "getk" : "getkt",
393 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
394 "datacache-postgres",
395 "Ending iteration (postgres error)\n");
400 if (0 == (cnt = PQntuples (res)))
404 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
405 "datacache-postgres",
406 "Ending iteration (no more results)\n");
416 if ( (3 != PQnfields (res)) ||
417 (sizeof (uint64_t) != PQfsize (res, 0)) ||
418 (sizeof (uint32_t) != PQfsize (res, 1)))
426 expiration_time.abs_value = GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, i, 0));
427 type = ntohl (*(uint32_t *) PQgetvalue (res, i, 1));
428 size = PQgetlength (res, i, 2);
430 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
431 "datacache-postgres",
432 "Found result of size %u bytes and type %u in database\n",
434 (unsigned int) type);
441 PQgetvalue (res, i, 2),
442 (enum GNUNET_BLOCK_Type) type))
445 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
446 "datacache-postgres",
447 "Ending iteration (client error)\n");
459 * Delete the entry with the lowest expiration value
460 * from the datacache right now.
462 * @param cls closure (our "struct Plugin")
463 * @return GNUNET_OK on success, GNUNET_SYSERR on error
466 postgres_plugin_del (void *cls)
468 struct Plugin *plugin = cls;
474 res = PQexecPrepared (plugin->dbh,
478 if (GNUNET_OK != check_result (plugin,
486 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
487 "datacache-postgres",
488 "Ending iteration (postgres error)\n");
492 if (0 == PQntuples (res))
496 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
497 "datacache-postgres",
498 "Ending iteration (no more results)\n");
501 return GNUNET_SYSERR;
503 if ( (3 != PQnfields (res)) ||
504 (sizeof (size) != PQfsize (res, 0)) ||
505 (sizeof (oid) != PQfsize (res, 1)) ||
506 (sizeof (GNUNET_HashCode) != PQgetlength (res, 0, 2)) )
512 size = ntohl (*(uint32_t *) PQgetvalue (res, 0, 0));
513 oid = ntohl (*(uint32_t *) PQgetvalue (res, 0, 1));
515 PQgetvalue (res, 0, 2),
516 sizeof (GNUNET_HashCode));
518 if (GNUNET_OK != delete_by_rowid (plugin, oid))
519 return GNUNET_SYSERR;
520 plugin->env->delete_notify (plugin->env->cls,
528 * Entry point for the plugin.
530 * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet")
531 * @return the plugin's closure (our "struct Plugin")
534 libgnunet_plugin_datacache_postgres_init (void *cls)
536 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
537 struct GNUNET_DATACACHE_PluginFunctions *api;
538 struct Plugin *plugin;
540 plugin = GNUNET_malloc (sizeof (struct Plugin));
544 init_connection (plugin))
546 GNUNET_free (plugin);
550 api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions));
552 api->get = &postgres_plugin_get;
553 api->put = &postgres_plugin_put;
554 api->del = &postgres_plugin_del;
555 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
556 "datacache-postgres",
557 _("Postgres datacache running\n"));
563 * Exit point from the plugin.
565 * @param cls closure (our "struct Plugin")
569 libgnunet_plugin_datacache_postgres_done (void *cls)
571 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
572 struct Plugin *plugin = api->cls;
574 PQfinish (plugin->dbh);
575 GNUNET_free (plugin);
582 /* end of plugin_datacache_postgres.c */