2 This file is part of GNUnet
3 Copyright (C) 2006, 2009, 2010, 2012 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_postgres_lib.h"
29 #include "gnunet_datacache_plugin.h"
31 #define LOG(kind,...) GNUNET_log_from (kind, "datacache-postgres", __VA_ARGS__)
34 * Per-entry overhead estimate
36 #define OVERHEAD (sizeof(struct 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 * @brief Get a database handle
59 * @param plugin global context
60 * @return GNUNET_OK on success, GNUNET_SYSERR on error
63 init_connection (struct Plugin *plugin)
67 plugin->dbh = GNUNET_POSTGRES_connect (plugin->env->cfg,
68 "datacache-postgres");
69 if (NULL == plugin->dbh)
73 "CREATE TEMPORARY TABLE gn090dc ("
74 " type INTEGER NOT NULL DEFAULT 0,"
75 " discard_time BIGINT NOT NULL DEFAULT 0,"
76 " key BYTEA NOT NULL DEFAULT '',"
77 " value BYTEA NOT NULL DEFAULT '',"
78 " path BYTEA DEFAULT '')"
81 ((PQresultStatus (ret) != PGRES_COMMAND_OK) &&
82 (0 != strcmp ("42P07", /* duplicate table */
87 (void) GNUNET_POSTGRES_check_result (plugin->dbh, ret,
88 PGRES_COMMAND_OK, "CREATE TABLE",
90 PQfinish (plugin->dbh);
94 if (PQresultStatus (ret) == PGRES_COMMAND_OK)
97 GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX idx_key ON gn090dc (key)")) ||
99 GNUNET_POSTGRES_exec (plugin->dbh, "CREATE INDEX idx_dt ON gn090dc (discard_time)")))
102 PQfinish (plugin->dbh);
104 return GNUNET_SYSERR;
110 "ALTER TABLE gn090dc ALTER value SET STORAGE EXTERNAL");
112 GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn090dc"))
114 PQfinish (plugin->dbh);
116 return GNUNET_SYSERR;
119 ret = PQexec (plugin->dbh, "ALTER TABLE gn090dc ALTER key SET STORAGE PLAIN");
121 GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "ALTER TABLE", "gn090dc"))
123 PQfinish (plugin->dbh);
125 return GNUNET_SYSERR;
129 GNUNET_POSTGRES_prepare (plugin->dbh, "getkt",
130 "SELECT discard_time,type,value,path FROM gn090dc "
131 "WHERE key=$1 AND type=$2 ", 2)) ||
133 GNUNET_POSTGRES_prepare (plugin->dbh, "getk",
134 "SELECT discard_time,type,value,path FROM gn090dc "
135 "WHERE key=$1", 1)) ||
137 GNUNET_POSTGRES_prepare (plugin->dbh, "getm",
138 "SELECT length(value),oid,key FROM gn090dc "
139 "ORDER BY discard_time ASC LIMIT 1", 0)) ||
141 GNUNET_POSTGRES_prepare (plugin->dbh, "delrow", "DELETE FROM gn090dc WHERE oid=$1", 1)) ||
143 GNUNET_POSTGRES_prepare (plugin->dbh, "put",
144 "INSERT INTO gn090dc (type, discard_time, key, value, path) "
145 "VALUES ($1, $2, $3, $4, $5)", 5)))
147 PQfinish (plugin->dbh);
149 return GNUNET_SYSERR;
156 * Store an item in the datastore.
158 * @param cls closure (our "struct Plugin")
159 * @param key key to store data under
160 * @param size number of bytes in data
161 * @param data data to store
162 * @param type type of the value
163 * @param discard_time when to discard the value in any case
164 * @param path_info_len number of entries in 'path_info'
165 * @param path_info a path through the network
166 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
169 postgres_plugin_put (void *cls, const struct GNUNET_HashCode * key, size_t size,
170 const char *data, enum GNUNET_BLOCK_Type type,
171 struct GNUNET_TIME_Absolute discard_time,
172 unsigned int path_info_len,
173 const struct GNUNET_PeerIdentity *path_info)
175 struct Plugin *plugin = cls;
177 uint32_t btype = htonl (type);
178 uint64_t bexpi = GNUNET_TIME_absolute_hton (discard_time).abs_value_us__;
180 const char *paramValues[] = {
181 (const char *) &btype,
182 (const char *) &bexpi,
185 (const char *) path_info
187 int paramLengths[] = {
190 sizeof (struct GNUNET_HashCode),
192 path_info_len * sizeof (struct GNUNET_PeerIdentity)
194 const int paramFormats[] = { 1, 1, 1, 1, 1 };
197 PQexecPrepared (plugin->dbh, "put", 5, paramValues, paramLengths,
200 GNUNET_POSTGRES_check_result (plugin->dbh, ret,
201 PGRES_COMMAND_OK, "PQexecPrepared", "put"))
204 return size + OVERHEAD;
209 * Iterate over the results for a particular key
212 * @param cls closure (our "struct Plugin")
214 * @param type entries of which type are relevant?
215 * @param iter maybe NULL (to just count)
216 * @param iter_cls closure for iter
217 * @return the number of results found
220 postgres_plugin_get (void *cls, const struct GNUNET_HashCode * key,
221 enum GNUNET_BLOCK_Type type,
222 GNUNET_DATACACHE_Iterator iter, void *iter_cls)
224 struct Plugin *plugin = cls;
225 uint32_t btype = htonl (type);
227 const char *paramValues[] = {
229 (const char *) &btype,
231 int paramLengths[] = {
232 sizeof (struct GNUNET_HashCode),
235 const int paramFormats[] = { 1, 1 };
236 struct GNUNET_TIME_Absolute expiration_time;
240 unsigned int path_len;
241 const struct GNUNET_PeerIdentity *path;
245 PQexecPrepared (plugin->dbh, (type == 0) ? "getk" : "getkt",
246 (type == 0) ? 1 : 2, paramValues, paramLengths,
249 GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK, "PQexecPrepared",
250 (type == 0) ? "getk" : "getkt"))
252 LOG (GNUNET_ERROR_TYPE_DEBUG,
253 "Ending iteration (postgres error)\n");
257 if (0 == (cnt = PQntuples (res)))
260 LOG (GNUNET_ERROR_TYPE_DEBUG,
261 "Ending iteration (no more results)\n");
270 if ((4 != PQnfields (res)) || (sizeof (uint64_t) != PQfsize (res, 0)) ||
271 (sizeof (uint32_t) != PQfsize (res, 1)))
277 for (i = 0; i < cnt; i++)
279 expiration_time.abs_value_us =
280 GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, i, 0));
281 type = ntohl (*(uint32_t *) PQgetvalue (res, i, 1));
282 size = PQgetlength (res, i, 2);
283 path_len = PQgetlength (res, i, 3);
284 if (0 != (path_len % sizeof (struct GNUNET_PeerIdentity)))
289 path_len %= sizeof (struct GNUNET_PeerIdentity);
290 path = (const struct GNUNET_PeerIdentity *) PQgetvalue (res, i, 3);
291 LOG (GNUNET_ERROR_TYPE_DEBUG,
292 "Found result of size %u bytes and type %u in database\n",
293 (unsigned int) size, (unsigned int) type);
295 iter (iter_cls, key, size, PQgetvalue (res, i, 2),
296 (enum GNUNET_BLOCK_Type) type,
301 LOG (GNUNET_ERROR_TYPE_DEBUG,
302 "Ending iteration (client error)\n");
313 * Delete the entry with the lowest expiration value
314 * from the datacache right now.
316 * @param cls closure (our "struct Plugin")
317 * @return GNUNET_OK on success, GNUNET_SYSERR on error
320 postgres_plugin_del (void *cls)
322 struct Plugin *plugin = cls;
325 struct GNUNET_HashCode key;
328 res = PQexecPrepared (plugin->dbh, "getm", 0, NULL, NULL, NULL, 1);
330 GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK, "PQexecPrepared", "getm"))
332 LOG (GNUNET_ERROR_TYPE_DEBUG,
333 "Ending iteration (postgres error)\n");
336 if (0 == PQntuples (res))
339 LOG (GNUNET_ERROR_TYPE_DEBUG,
340 "Ending iteration (no more results)\n");
342 return GNUNET_SYSERR;
344 if ((3 != PQnfields (res)) || (sizeof (size) != PQfsize (res, 0)) ||
345 (sizeof (oid) != PQfsize (res, 1)) ||
346 (sizeof (struct GNUNET_HashCode) != PQgetlength (res, 0, 2)))
352 size = ntohl (*(uint32_t *) PQgetvalue (res, 0, 0));
353 oid = ntohl (*(uint32_t *) PQgetvalue (res, 0, 1));
354 memcpy (&key, PQgetvalue (res, 0, 2), sizeof (struct GNUNET_HashCode));
356 if (GNUNET_OK != GNUNET_POSTGRES_delete_by_rowid (plugin->dbh, "delrow", oid))
357 return GNUNET_SYSERR;
358 plugin->env->delete_notify (plugin->env->cls, &key, size + OVERHEAD);
364 * Entry point for the plugin.
366 * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet")
367 * @return the plugin's closure (our "struct Plugin")
370 libgnunet_plugin_datacache_postgres_init (void *cls)
372 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
373 struct GNUNET_DATACACHE_PluginFunctions *api;
374 struct Plugin *plugin;
376 plugin = GNUNET_new (struct Plugin);
379 if (GNUNET_OK != init_connection (plugin))
381 GNUNET_free (plugin);
385 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
387 api->get = &postgres_plugin_get;
388 api->put = &postgres_plugin_put;
389 api->del = &postgres_plugin_del;
390 LOG (GNUNET_ERROR_TYPE_INFO,
391 _("Postgres datacache running\n"));
397 * Exit point from the plugin.
399 * @param cls closure (our "struct Plugin")
403 libgnunet_plugin_datacache_postgres_done (void *cls)
405 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
406 struct Plugin *plugin = api->cls;
408 PQfinish (plugin->dbh);
409 GNUNET_free (plugin);
416 /* end of plugin_datacache_postgres.c */