diff options
author | Christian Grothoff <christian@grothoff.org> | 2010-08-26 13:32:21 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2010-08-26 13:32:21 +0000 |
commit | d90d66f22e4b3f227067a8f06cc7e0e994a38c2c (patch) | |
tree | 45737f029aa36bcdfec60f93ef9cc085a5a1f97a /src/datacache | |
parent | deed215d0d135d30beac950a5f88050497ef9e39 (diff) | |
download | gnunet-d90d66f22e4b3f227067a8f06cc7e0e994a38c2c.tar.gz gnunet-d90d66f22e4b3f227067a8f06cc7e0e994a38c2c.zip |
towards datacache
Diffstat (limited to 'src/datacache')
-rw-r--r-- | src/datacache/plugin_datacache_postgres.c | 519 |
1 files changed, 519 insertions, 0 deletions
diff --git a/src/datacache/plugin_datacache_postgres.c b/src/datacache/plugin_datacache_postgres.c new file mode 100644 index 000000000..5959d54a6 --- /dev/null +++ b/src/datacache/plugin_datacache_postgres.c | |||
@@ -0,0 +1,519 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet | ||
3 | (C) 2006, 2009, 2010 Christian Grothoff (and other contributing authors) | ||
4 | |||
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. | ||
9 | |||
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. | ||
14 | |||
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. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file datacache/plugin_datacache_postgres.c | ||
23 | * @brief postgres for an implementation of a database backend for the datacache | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "plugin_datacache.h" | ||
29 | #include <postgresql/libpq-fe.h> | ||
30 | |||
31 | #define DEBUG_POSTGRES GNUNET_NO | ||
32 | |||
33 | |||
34 | |||
35 | /** | ||
36 | * Context for all functions in this plugin. | ||
37 | */ | ||
38 | struct Plugin | ||
39 | { | ||
40 | /** | ||
41 | * Our execution environment. | ||
42 | */ | ||
43 | struct GNUNET_DATACACHE_PluginEnvironment *env; | ||
44 | |||
45 | /** | ||
46 | * Native Postgres database handle. | ||
47 | */ | ||
48 | PGconn *dbh; | ||
49 | |||
50 | }; | ||
51 | |||
52 | |||
53 | /** | ||
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. | ||
57 | * | ||
58 | * @return GNUNET_OK if the result is acceptable | ||
59 | */ | ||
60 | static int | ||
61 | check_result (struct Plugin *plugin, | ||
62 | PGresult * ret, | ||
63 | int expected_status, | ||
64 | const char *command, const char *args, int line) | ||
65 | { | ||
66 | if (ret == NULL) | ||
67 | { | ||
68 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
69 | "datastore-postgres", | ||
70 | "Postgres failed to allocate result for `%s:%s' at %d\n", | ||
71 | command, args, line); | ||
72 | return GNUNET_SYSERR; | ||
73 | } | ||
74 | if (PQresultStatus (ret) != expected_status) | ||
75 | { | ||
76 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
77 | "datastore-postgres", | ||
78 | _("`%s:%s' failed at %s:%d with error: %s"), | ||
79 | command, args, __FILE__, line, PQerrorMessage (plugin->dbh)); | ||
80 | PQclear (ret); | ||
81 | return GNUNET_SYSERR; | ||
82 | } | ||
83 | return GNUNET_OK; | ||
84 | } | ||
85 | |||
86 | |||
87 | /** | ||
88 | * Run simple SQL statement (without results). | ||
89 | */ | ||
90 | static int | ||
91 | pq_exec (struct Plugin *plugin, | ||
92 | const char *sql, int line) | ||
93 | { | ||
94 | PGresult *ret; | ||
95 | ret = PQexec (plugin->dbh, sql); | ||
96 | if (GNUNET_OK != check_result (plugin, | ||
97 | ret, | ||
98 | PGRES_COMMAND_OK, "PQexec", sql, line)) | ||
99 | return GNUNET_SYSERR; | ||
100 | PQclear (ret); | ||
101 | return GNUNET_OK; | ||
102 | } | ||
103 | |||
104 | |||
105 | /** | ||
106 | * Prepare SQL statement. | ||
107 | */ | ||
108 | static int | ||
109 | pq_prepare (struct Plugin *plugin, | ||
110 | const char *name, const char *sql, int nparms, int line) | ||
111 | { | ||
112 | PGresult *ret; | ||
113 | ret = PQprepare (plugin->dbh, name, sql, nparms, NULL); | ||
114 | if (GNUNET_OK != | ||
115 | check_result (plugin, | ||
116 | ret, PGRES_COMMAND_OK, "PQprepare", sql, line)) | ||
117 | return GNUNET_SYSERR; | ||
118 | PQclear (ret); | ||
119 | return GNUNET_OK; | ||
120 | } | ||
121 | |||
122 | |||
123 | /** | ||
124 | * @brief Get a database handle | ||
125 | * @return GNUNET_OK on success, GNUNET_SYSERR on error | ||
126 | */ | ||
127 | static int | ||
128 | init_connection (struct Plugin *plugin) | ||
129 | { | ||
130 | char *conninfo; | ||
131 | PGresult *ret; | ||
132 | |||
133 | /* Open database and precompile statements */ | ||
134 | conninfo = NULL; | ||
135 | GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, | ||
136 | "datacache-postgres", | ||
137 | "CONFIG", | ||
138 | &conninfo); | ||
139 | plugin->dbh = PQconnectdb (conninfo == NULL ? "" : conninfo); | ||
140 | GNUNET_free_non_null (conninfo); | ||
141 | if (NULL == plugin->dbh) | ||
142 | { | ||
143 | /* FIXME: warn about out-of-memory? */ | ||
144 | return GNUNET_SYSERR; | ||
145 | } | ||
146 | if (PQstatus (plugin->dbh) != CONNECTION_OK) | ||
147 | { | ||
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); | ||
153 | plugin->dbh = NULL; | ||
154 | return GNUNET_SYSERR; | ||
155 | } | ||
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 */ | ||
165 | PQresultErrorField | ||
166 | (ret, | ||
167 | PG_DIAG_SQLSTATE))))) | ||
168 | { | ||
169 | check_result (plugin, | ||
170 | ret, PGRES_COMMAND_OK, "CREATE TABLE", "gn090dc", __LINE__); | ||
171 | PQfinish (plugin->dbh); | ||
172 | plugin->dbh = NULL; | ||
173 | return GNUNET_SYSERR; | ||
174 | } | ||
175 | if (PQresultStatus (ret) == PGRES_COMMAND_OK) | ||
176 | { | ||
177 | if ((GNUNET_OK != | ||
178 | pq_exec (plugin, "CREATE INDEX idx_key ON gn090dc (key)", __LINE__)) || | ||
179 | (GNUNET_OK != | ||
180 | pq_exec (plugin, "CREATE INDEX idx_dt ON gn090 (discard_time)", | ||
181 | __LINE__)) ) | ||
182 | { | ||
183 | PQclear (ret); | ||
184 | PQfinish (plugin->dbh); | ||
185 | plugin->dbh = NULL; | ||
186 | return GNUNET_SYSERR; | ||
187 | } | ||
188 | } | ||
189 | PQclear (ret); | ||
190 | #if 1 | ||
191 | ret = PQexec (plugin->dbh, | ||
192 | "ALTER TABLE gn090dc ALTER value SET STORAGE EXTERNAL"); | ||
193 | if (GNUNET_OK != | ||
194 | check_result (plugin, | ||
195 | ret, PGRES_COMMAND_OK, | ||
196 | "ALTER TABLE", "gn090dc", __LINE__)) | ||
197 | { | ||
198 | PQfinish (plugin->dbh); | ||
199 | plugin->dbh = NULL; | ||
200 | return GNUNET_SYSERR; | ||
201 | } | ||
202 | PQclear (ret); | ||
203 | ret = PQexec (plugin->dbh, | ||
204 | "ALTER TABLE gn090dc ALTER key SET STORAGE PLAIN"); | ||
205 | if (GNUNET_OK != | ||
206 | check_result (plugin, | ||
207 | ret, PGRES_COMMAND_OK, | ||
208 | "ALTER TABLE", "gn090dc", __LINE__)) | ||
209 | { | ||
210 | PQfinish (plugin->dbh); | ||
211 | plugin->dbh = NULL; | ||
212 | return GNUNET_SYSERR; | ||
213 | } | ||
214 | PQclear (ret); | ||
215 | #endif | ||
216 | if ((GNUNET_OK != | ||
217 | pq_prepare (plugin, | ||
218 | "getkt", | ||
219 | "SELECT discard_time,type,value FROM gn090dc " | ||
220 | "WHERE hash=$1 type=$2 ", | ||
221 | 2, | ||
222 | __LINE__)) || | ||
223 | (GNUNET_OK != | ||
224 | pq_prepare (plugin, | ||
225 | "getk", | ||
226 | "SELECT discard_time,type,value FROM gn090dc " | ||
227 | "WHERE hash=$1", | ||
228 | 1, | ||
229 | __LINE__)) || | ||
230 | (GNUNET_OK != | ||
231 | pq_prepare (plugin, | ||
232 | "getm", | ||
233 | "SELECT length(value),oid FROM gn090dc" | ||
234 | "ORDER BY discard_time ASC LIMIT 1", | ||
235 | 0, | ||
236 | __LINE__)) || | ||
237 | (GNUNET_OK != | ||
238 | pq_prepare (plugin, | ||
239 | "delrow", | ||
240 | "DELETE FROM gn090dc WHERE oid=$1", | ||
241 | 1, | ||
242 | __LINE__)) || | ||
243 | (GNUNET_OK != | ||
244 | pq_prepare (plugin, | ||
245 | "put", | ||
246 | "INSERT INTO gn090dc (type, discard_time, key, value) " | ||
247 | "VALUES ($1, $2, $3, $4)", | ||
248 | 4, | ||
249 | __LINE__)) ) | ||
250 | { | ||
251 | PQfinish (plugin->dbh); | ||
252 | plugin->dbh = NULL; | ||
253 | return GNUNET_SYSERR; | ||
254 | } | ||
255 | return GNUNET_OK; | ||
256 | } | ||
257 | |||
258 | |||
259 | /** | ||
260 | * Delete the row identified by the given rowid (qid | ||
261 | * in postgres). | ||
262 | * | ||
263 | * @return GNUNET_OK on success | ||
264 | */ | ||
265 | static int | ||
266 | delete_by_rowid (struct Plugin *plugin, | ||
267 | unsigned int rowid) | ||
268 | { | ||
269 | const char *paramValues[] = { (const char *) &rowid }; | ||
270 | int paramLengths[] = { sizeof (rowid) }; | ||
271 | const int paramFormats[] = { 1 }; | ||
272 | PGresult *ret; | ||
273 | |||
274 | ret = PQexecPrepared (plugin->dbh, | ||
275 | "delrow", | ||
276 | 1, paramValues, paramLengths, paramFormats, 1); | ||
277 | if (GNUNET_OK != | ||
278 | check_result (plugin, | ||
279 | ret, PGRES_COMMAND_OK, "PQexecPrepared", "delrow", | ||
280 | __LINE__)) | ||
281 | { | ||
282 | return GNUNET_SYSERR; | ||
283 | } | ||
284 | PQclear (ret); | ||
285 | return GNUNET_OK; | ||
286 | } | ||
287 | |||
288 | |||
289 | /** | ||
290 | * Store an item in the datastore. | ||
291 | * | ||
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 | ||
299 | */ | ||
300 | static uint32_t | ||
301 | postgres_plugin_put (void *cls, | ||
302 | const GNUNET_HashCode * key, | ||
303 | uint32_t size, | ||
304 | const char *data, | ||
305 | enum GNUNET_BLOCK_Type type, | ||
306 | struct GNUNET_TIME_Absolute discard_time) | ||
307 | { | ||
308 | struct Plugin *plugin = cls; | ||
309 | PGresult *ret; | ||
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, | ||
315 | (const char *) key, | ||
316 | (const char *) data | ||
317 | }; | ||
318 | int paramLengths[] = { | ||
319 | sizeof (btype), | ||
320 | sizeof (bexpi), | ||
321 | sizeof (GNUNET_HashCode), | ||
322 | size | ||
323 | }; | ||
324 | const int paramFormats[] = { 1, 1, 1, 1 }; | ||
325 | |||
326 | ret = PQexecPrepared (plugin->dbh, | ||
327 | "put", 4, paramValues, paramLengths, paramFormats, 1); | ||
328 | if (GNUNET_OK != check_result (plugin, ret, | ||
329 | PGRES_COMMAND_OK, | ||
330 | "PQexecPrepared", "put", __LINE__)) | ||
331 | return GNUNET_SYSERR; | ||
332 | PQclear (ret); | ||
333 | return size; | ||
334 | } | ||
335 | |||
336 | |||
337 | /** | ||
338 | * Iterate over the results for a particular key | ||
339 | * in the datastore. | ||
340 | * | ||
341 | * @param cls closure (our "struct Plugin") | ||
342 | * @param key | ||
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 | ||
347 | */ | ||
348 | static unsigned int | ||
349 | postgres_plugin_get (void *cls, | ||
350 | const GNUNET_HashCode * key, | ||
351 | enum GNUNET_BLOCK_Type type, | ||
352 | GNUNET_DATACACHE_Iterator iter, | ||
353 | void *iter_cls) | ||
354 | { | ||
355 | struct Plugin *plugin = cls; | ||
356 | uint32_t btype = htonl (type); | ||
357 | const char *paramValues[] = { | ||
358 | (const char *) key, | ||
359 | (const char *) &btype, | ||
360 | }; | ||
361 | int paramLengths[] = { | ||
362 | sizeof (GNUNET_HashCode), | ||
363 | sizeof (btype), | ||
364 | }; | ||
365 | const int paramFormats[] = { 1, 1 }; | ||
366 | struct GNUNET_TIME_Absolute expiration_time; | ||
367 | uint32_t size; | ||
368 | unsigned int cnt; | ||
369 | unsigned int i; | ||
370 | PGresult *res; | ||
371 | |||
372 | cnt = 0; | ||
373 | res = PQexecPrepared (plugin->dbh, | ||
374 | (type == 0) ? "getk" : "getkt", | ||
375 | (type == 0) ? 1 : 2, | ||
376 | paramValues, | ||
377 | paramLengths, | ||
378 | paramFormats, | ||
379 | 1); | ||
380 | if (GNUNET_OK != check_result (plugin, | ||
381 | res, | ||
382 | PGRES_TUPLES_OK, | ||
383 | "PQexecPrepared", | ||
384 | (type == 0) ? "getk" : "getkt", | ||
385 | __LINE__)) | ||
386 | { | ||
387 | #if DEBUG_POSTGRES | ||
388 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
389 | "datacache-postgres", | ||
390 | "Ending iteration (postgres error)\n"); | ||
391 | #endif | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | if (0 == (cnt = PQntuples (res))) | ||
396 | { | ||
397 | /* no result */ | ||
398 | #if DEBUG_POSTGRES | ||
399 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
400 | "datacache-postgres", | ||
401 | "Ending iteration (no more results)\n"); | ||
402 | #endif | ||
403 | PQclear (res); | ||
404 | return 0; | ||
405 | } | ||
406 | if ( (3 != PQnfields (res)) || | ||
407 | (sizeof (uint64_t) != PQfsize (res, 0)) || | ||
408 | (sizeof (uint32_t) != PQfsize (res, 1))) | ||
409 | { | ||
410 | GNUNET_break (0); | ||
411 | PQclear (res); | ||
412 | return 0; | ||
413 | } | ||
414 | for (i=0;i<cnt;i++) | ||
415 | { | ||
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); | ||
419 | #if DEBUG_POSTGRES | ||
420 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
421 | "datacache-postgres", | ||
422 | "Found result of size %u bytes and type %u in database\n", | ||
423 | (unsigned int) size, | ||
424 | (unsigned int) type); | ||
425 | #endif | ||
426 | if (GNUNET_SYSERR == | ||
427 | iter (iter_cls, | ||
428 | expiration_time, | ||
429 | key, | ||
430 | size, | ||
431 | PQgetvalue (res, i, 2), | ||
432 | (enum GNUNET_BLOCK_Type) type)) | ||
433 | { | ||
434 | #if DEBUG_POSTGRES | ||
435 | GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, | ||
436 | "datacache-postgres", | ||
437 | "Ending iteration (client error)\n"); | ||
438 | #endif | ||
439 | PQclear (res); | ||
440 | return cnt; | ||
441 | } | ||
442 | } | ||
443 | PQclear (res); | ||
444 | return cnt; | ||
445 | } | ||
446 | |||
447 | |||
448 | /** | ||
449 | * Delete the entry with the lowest expiration value | ||
450 | * from the datacache right now. | ||
451 | * | ||
452 | * @param cls closure (our "struct Plugin") | ||
453 | * @return GNUNET_OK on success, GNUNET_SYSERR on error | ||
454 | */ | ||
455 | static int | ||
456 | postgres_plugin_del (void *cls) | ||
457 | { | ||
458 | |||
459 | GNUNET_break (0); | ||
460 | return GNUNET_SYSERR; | ||
461 | } | ||
462 | |||
463 | |||
464 | /** | ||
465 | * Entry point for the plugin. | ||
466 | * | ||
467 | * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet") | ||
468 | * @return the plugin's closure (our "struct Plugin") | ||
469 | */ | ||
470 | void * | ||
471 | libgnunet_plugin_datacache_postgres_init (void *cls) | ||
472 | { | ||
473 | struct GNUNET_DATACACHE_PluginEnvironment *env = cls; | ||
474 | struct GNUNET_DATACACHE_PluginFunctions *api; | ||
475 | struct Plugin *plugin; | ||
476 | |||
477 | plugin = GNUNET_malloc (sizeof (struct Plugin)); | ||
478 | plugin->env = env; | ||
479 | |||
480 | if (GNUNET_OK != | ||
481 | init_connection (plugin)) | ||
482 | { | ||
483 | GNUNET_free (plugin); | ||
484 | return NULL; | ||
485 | } | ||
486 | |||
487 | api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions)); | ||
488 | api->cls = plugin; | ||
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")); | ||
495 | return api; | ||
496 | } | ||
497 | |||
498 | |||
499 | /** | ||
500 | * Exit point from the plugin. | ||
501 | * | ||
502 | * @param cls closure (our "struct Plugin") | ||
503 | * @return NULL | ||
504 | */ | ||
505 | void * | ||
506 | libgnunet_plugin_datacache_postgres_done (void *cls) | ||
507 | { | ||
508 | struct GNUNET_DATACACHE_PluginFunctions *api = cls; | ||
509 | struct Plugin *plugin = api->cls; | ||
510 | |||
511 | GNUNET_free (plugin); | ||
512 | GNUNET_free (api); | ||
513 | return NULL; | ||
514 | } | ||
515 | |||
516 | |||
517 | |||
518 | /* end of plugin_datacache_postgres.c */ | ||
519 | |||