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
36 * Context for all functions in this plugin.
41 * Our execution environment.
43 struct GNUNET_DATACACHE_PluginEnvironment *env;
46 * Native Postgres database handle.
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.
58 * @return GNUNET_OK if the result is acceptable
61 check_result (struct Plugin *plugin,
64 const char *command, const char *args, int line)
68 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
70 "Postgres failed to allocate result for `%s:%s' at %d\n",
74 if (PQresultStatus (ret) != expected_status)
76 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
78 _("`%s:%s' failed at %s:%d with error: %s"),
79 command, args, __FILE__, line, PQerrorMessage (plugin->dbh));
88 * Run simple SQL statement (without results).
91 pq_exec (struct Plugin *plugin,
92 const char *sql, int line)
95 ret = PQexec (plugin->dbh, sql);
96 if (GNUNET_OK != check_result (plugin,
98 PGRES_COMMAND_OK, "PQexec", sql, line))
106 * Prepare SQL statement.
109 pq_prepare (struct Plugin *plugin,
110 const char *name, const char *sql, int nparms, int line)
113 ret = PQprepare (plugin->dbh, name, sql, nparms, NULL);
115 check_result (plugin,
116 ret, PGRES_COMMAND_OK, "PQprepare", sql, line))
117 return GNUNET_SYSERR;
124 * @brief Get a database handle
125 * @return GNUNET_OK on success, GNUNET_SYSERR on error
128 init_connection (struct Plugin *plugin)
133 /* Open database and precompile statements */
135 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
136 "datacache-postgres",
139 plugin->dbh = PQconnectdb (conninfo == NULL ? "" : conninfo);
140 GNUNET_free_non_null (conninfo);
141 if (NULL == plugin->dbh)
143 /* FIXME: warn about out-of-memory? */
144 return GNUNET_SYSERR;
146 if (PQstatus (plugin->dbh) != CONNECTION_OK)
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);
154 return GNUNET_SYSERR;
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 */
167 PG_DIAG_SQLSTATE)))))
169 check_result (plugin,
170 ret, PGRES_COMMAND_OK, "CREATE TABLE", "gn090dc", __LINE__);
171 PQfinish (plugin->dbh);
173 return GNUNET_SYSERR;
175 if (PQresultStatus (ret) == PGRES_COMMAND_OK)
178 pq_exec (plugin, "CREATE INDEX idx_key ON gn090dc (key)", __LINE__)) ||
180 pq_exec (plugin, "CREATE INDEX idx_dt ON gn090 (discard_time)",
184 PQfinish (plugin->dbh);
186 return GNUNET_SYSERR;
191 ret = PQexec (plugin->dbh,
192 "ALTER TABLE gn090dc ALTER value SET STORAGE EXTERNAL");
194 check_result (plugin,
195 ret, PGRES_COMMAND_OK,
196 "ALTER TABLE", "gn090dc", __LINE__))
198 PQfinish (plugin->dbh);
200 return GNUNET_SYSERR;
203 ret = PQexec (plugin->dbh,
204 "ALTER TABLE gn090dc ALTER key SET STORAGE PLAIN");
206 check_result (plugin,
207 ret, PGRES_COMMAND_OK,
208 "ALTER TABLE", "gn090dc", __LINE__))
210 PQfinish (plugin->dbh);
212 return GNUNET_SYSERR;
219 "SELECT discard_time,type,value FROM gn090dc "
220 "WHERE hash=$1 type=$2 ",
226 "SELECT discard_time,type,value FROM gn090dc "
233 "SELECT length(value),oid FROM gn090dc"
234 "ORDER BY discard_time ASC LIMIT 1",
240 "DELETE FROM gn090dc WHERE oid=$1",
246 "INSERT INTO gn090dc (type, discard_time, key, value) "
247 "VALUES ($1, $2, $3, $4)",
251 PQfinish (plugin->dbh);
253 return GNUNET_SYSERR;
260 * Delete the row identified by the given rowid (qid
263 * @return GNUNET_OK on success
266 delete_by_rowid (struct Plugin *plugin,
269 const char *paramValues[] = { (const char *) &rowid };
270 int paramLengths[] = { sizeof (rowid) };
271 const int paramFormats[] = { 1 };
274 ret = PQexecPrepared (plugin->dbh,
276 1, paramValues, paramLengths, paramFormats, 1);
278 check_result (plugin,
279 ret, PGRES_COMMAND_OK, "PQexecPrepared", "delrow",
282 return GNUNET_SYSERR;
290 * Store an item in the datastore.
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
301 postgres_plugin_put (void *cls,
302 const GNUNET_HashCode * key,
305 enum GNUNET_BLOCK_Type type,
306 struct GNUNET_TIME_Absolute discard_time)
308 struct Plugin *plugin = cls;
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,
318 int paramLengths[] = {
321 sizeof (GNUNET_HashCode),
324 const int paramFormats[] = { 1, 1, 1, 1 };
326 ret = PQexecPrepared (plugin->dbh,
327 "put", 4, paramValues, paramLengths, paramFormats, 1);
328 if (GNUNET_OK != check_result (plugin, ret,
330 "PQexecPrepared", "put", __LINE__))
331 return GNUNET_SYSERR;
338 * Iterate over the results for a particular key
341 * @param cls closure (our "struct Plugin")
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
349 postgres_plugin_get (void *cls,
350 const GNUNET_HashCode * key,
351 enum GNUNET_BLOCK_Type type,
352 GNUNET_DATACACHE_Iterator iter,
355 struct Plugin *plugin = cls;
356 uint32_t btype = htonl (type);
357 const char *paramValues[] = {
359 (const char *) &btype,
361 int paramLengths[] = {
362 sizeof (GNUNET_HashCode),
365 const int paramFormats[] = { 1, 1 };
366 struct GNUNET_TIME_Absolute expiration_time;
373 res = PQexecPrepared (plugin->dbh,
374 (type == 0) ? "getk" : "getkt",
380 if (GNUNET_OK != check_result (plugin,
384 (type == 0) ? "getk" : "getkt",
388 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
389 "datacache-postgres",
390 "Ending iteration (postgres error)\n");
395 if (0 == (cnt = PQntuples (res)))
399 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
400 "datacache-postgres",
401 "Ending iteration (no more results)\n");
406 if ( (3 != PQnfields (res)) ||
407 (sizeof (uint64_t) != PQfsize (res, 0)) ||
408 (sizeof (uint32_t) != PQfsize (res, 1)))
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);
420 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
421 "datacache-postgres",
422 "Found result of size %u bytes and type %u in database\n",
424 (unsigned int) type);
431 PQgetvalue (res, i, 2),
432 (enum GNUNET_BLOCK_Type) type))
435 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
436 "datacache-postgres",
437 "Ending iteration (client error)\n");
449 * Delete the entry with the lowest expiration value
450 * from the datacache right now.
452 * @param cls closure (our "struct Plugin")
453 * @return GNUNET_OK on success, GNUNET_SYSERR on error
456 postgres_plugin_del (void *cls)
460 return GNUNET_SYSERR;
465 * Entry point for the plugin.
467 * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet")
468 * @return the plugin's closure (our "struct Plugin")
471 libgnunet_plugin_datacache_postgres_init (void *cls)
473 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
474 struct GNUNET_DATACACHE_PluginFunctions *api;
475 struct Plugin *plugin;
477 plugin = GNUNET_malloc (sizeof (struct Plugin));
481 init_connection (plugin))
483 GNUNET_free (plugin);
487 api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions));
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"));
500 * Exit point from the plugin.
502 * @param cls closure (our "struct Plugin")
506 libgnunet_plugin_datacache_postgres_done (void *cls)
508 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
509 struct Plugin *plugin = api->cls;
511 GNUNET_free (plugin);
518 /* end of plugin_datacache_postgres.c */