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");
412 if ( (3 != PQnfields (res)) ||
413 (sizeof (uint64_t) != PQfsize (res, 0)) ||
414 (sizeof (uint32_t) != PQfsize (res, 1)))
422 expiration_time.value = GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, i, 0));
423 type = ntohl (*(uint32_t *) PQgetvalue (res, i, 1));
424 size = PQgetlength (res, i, 2);
426 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
427 "datacache-postgres",
428 "Found result of size %u bytes and type %u in database\n",
430 (unsigned int) type);
437 PQgetvalue (res, i, 2),
438 (enum GNUNET_BLOCK_Type) type))
441 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
442 "datacache-postgres",
443 "Ending iteration (client error)\n");
455 * Delete the entry with the lowest expiration value
456 * from the datacache right now.
458 * @param cls closure (our "struct Plugin")
459 * @return GNUNET_OK on success, GNUNET_SYSERR on error
462 postgres_plugin_del (void *cls)
464 struct Plugin *plugin = cls;
470 res = PQexecPrepared (plugin->dbh,
474 if (GNUNET_OK != check_result (plugin,
482 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
483 "datacache-postgres",
484 "Ending iteration (postgres error)\n");
488 if (0 == PQntuples (res))
492 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
493 "datacache-postgres",
494 "Ending iteration (no more results)\n");
497 return GNUNET_SYSERR;
499 if ( (3 != PQnfields (res)) ||
500 (sizeof (size) != PQfsize (res, 0)) ||
501 (sizeof (oid) != PQfsize (res, 1)) ||
502 (sizeof (GNUNET_HashCode) != PQgetlength (res, 0, 2)) )
508 size = ntohl (*(uint32_t *) PQgetvalue (res, 0, 0));
509 oid = ntohl (*(uint32_t *) PQgetvalue (res, 0, 1));
511 PQgetvalue (res, 0, 2),
512 sizeof (GNUNET_HashCode));
514 if (GNUNET_OK != delete_by_rowid (plugin, oid))
515 return GNUNET_SYSERR;
516 plugin->env->delete_notify (plugin->env->cls,
524 * Entry point for the plugin.
526 * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet")
527 * @return the plugin's closure (our "struct Plugin")
530 libgnunet_plugin_datacache_postgres_init (void *cls)
532 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
533 struct GNUNET_DATACACHE_PluginFunctions *api;
534 struct Plugin *plugin;
536 plugin = GNUNET_malloc (sizeof (struct Plugin));
540 init_connection (plugin))
542 GNUNET_free (plugin);
546 api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions));
548 api->get = &postgres_plugin_get;
549 api->put = &postgres_plugin_put;
550 api->del = &postgres_plugin_del;
551 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
552 "datacache-postgres",
553 _("Postgres datacache running\n"));
559 * Exit point from the plugin.
561 * @param cls closure (our "struct Plugin")
565 libgnunet_plugin_datacache_postgres_done (void *cls)
567 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
568 struct Plugin *plugin = api->cls;
570 PQfinish (plugin->dbh);
571 GNUNET_free (plugin);
578 /* end of plugin_datacache_postgres.c */