fixed #2987 -- at least the testcases now pass
[oweals/gnunet.git] / src / namestore / plugin_namestore_postgres.c
1  /*
2   * This file is part of GNUnet
3   * (C) 2009-2013 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 namestore/plugin_namestore_postgres.c
23  * @brief postgres-based namestore backend
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_namestore_plugin.h"
28 #include "gnunet_namestore_service.h"
29 #include "gnunet_postgres_lib.h"
30 #include "namestore.h"
31
32
33 /**
34  * After how many ms "busy" should a DB operation fail for good?
35  * A low value makes sure that we are more responsive to requests
36  * (especially PUTs).  A high value guarantees a higher success
37  * rate (SELECTs in iterate can take several seconds despite LIMIT=1).
38  *
39  * The default value of 1s should ensure that users do not experience
40  * huge latencies while at the same time allowing operations to succeed
41  * with reasonable probability.
42  */
43 #define BUSY_TIMEOUT_MS 1000
44
45
46 /**
47  * Log an error message at log-level 'level' that indicates
48  * a failure of the command 'cmd' on file 'filename'
49  * with the message given by strerror(errno).
50  */
51 #define LOG_POSTGRES(db, level, cmd) do { GNUNET_log_from (level, "namestore-postgres", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while(0)
52
53 #define LOG(kind,...) GNUNET_log_from (kind, "namestore-postgres", __VA_ARGS__)
54
55
56 /**
57  * Context for all functions in this plugin.
58  */
59 struct Plugin
60 {
61
62   const struct GNUNET_CONFIGURATION_Handle *cfg;
63
64   /**
65    * Native Postgres database handle.
66    */
67   PGconn *dbh;
68
69 };
70
71
72 /**
73  * Create our database indices.
74  *
75  * @param dbh handle to the database
76  */
77 static void
78 create_indices (PGconn * dbh)
79 {
80   /* create indices */
81   if ( (GNUNET_OK !=
82         GNUNET_POSTGRES_exec (dbh,
83                               "CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)")) ||
84        (GNUNET_OK !=
85         GNUNET_POSTGRES_exec (dbh,
86                               "CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)")) ||
87        (GNUNET_OK !=
88         GNUNET_POSTGRES_exec (dbh,
89                               "CREATE INDEX ir_pkey_reverse ON ns097records (zone_private_key,pkey)")) ||
90        (GNUNET_OK !=
91         GNUNET_POSTGRES_exec (dbh,
92                               "CREATE INDEX ir_pkey_iter ON ns097records (zone_private_key,rvalue)")) ||
93        (GNUNET_OK !=
94         GNUNET_POSTGRES_exec (dbh,
95                               "CREATE INDEX it_iter ON ns097records (rvalue)")) )
96     LOG (GNUNET_ERROR_TYPE_ERROR,
97          _("Failed to create indices\n"));
98 }
99
100
101 /**
102  * Initialize the database connections and associated
103  * data structures (create tables and indices
104  * as needed as well).
105  *
106  * @param plugin the plugin context (state for this module)
107  * @return GNUNET_OK on success
108  */
109 static int
110 database_setup (struct Plugin *plugin)
111 {
112   PGresult *res;
113
114   plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg,
115                                          "namestore-postgres");
116   if (NULL == plugin->dbh)
117     return GNUNET_SYSERR;
118   if (GNUNET_YES ==
119       GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
120                                             "namestore-postgres",
121                                             "TEMPORARY_TABLE"))
122   {
123     res =
124       PQexec (plugin->dbh,
125               "CREATE TEMPORARY TABLE ns097records ("
126               " zone_private_key BYTEA NOT NULL DEFAULT '',"
127               " pkey BYTEA DEFAULT '',"
128               " rvalue BYTEA NOT NULL DEFAULT '',"
129               " record_count INTEGER NOT NULL DEFAULT 0,"
130               " record_data BYTEA NOT NULL DEFAULT '',"
131               " label TEXT NOT NULL DEFAULT ''"
132               ")" "WITH OIDS");
133   }
134   else
135   {
136     res =
137       PQexec (plugin->dbh,
138               "CREATE TABLE ns097records ("
139               " zone_private_key BYTEA NOT NULL DEFAULT '',"
140               " pkey BYTEA DEFAULT '',"
141               " rvalue BYTEA NOT NULL DEFAULT '',"
142               " record_count INTEGER NOT NULL DEFAULT 0,"
143               " record_data BYTEA NOT NULL DEFAULT '',"
144               " label TEXT NOT NULL DEFAULT ''"
145               ")" "WITH OIDS");
146   }
147   if ( (NULL == res) ||
148        ((PQresultStatus (res) != PGRES_COMMAND_OK) &&
149         (0 != strcmp ("42P07",    /* duplicate table */
150                       PQresultErrorField
151                       (res,
152                        PG_DIAG_SQLSTATE)))))
153   {
154     (void) GNUNET_POSTGRES_check_result (plugin->dbh, res,
155                                          PGRES_COMMAND_OK, "CREATE TABLE",
156                                          "ns097records");
157     PQfinish (plugin->dbh);
158     plugin->dbh = NULL;
159     return GNUNET_SYSERR;
160   }
161
162
163   if (GNUNET_YES ==
164       GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
165                                             "namestore-postgres",
166                                             "TEMPORARY_TABLE"))
167   {
168     res =
169       PQexec (plugin->dbh,
170               "CREATE TEMPORARY TABLE ns096blocks ("
171               " query BYTEA NOT NULL DEFAULT '',"
172               " block BYTEA NOT NULL DEFAULT '',"
173               " expiration_time BIGINT NOT NULL DEFAULT 0"
174               ")" "WITH OIDS");
175   }
176   else
177   {
178     res =
179       PQexec (plugin->dbh,
180               "CREATE TABLE ns096blocks ("
181               " query BYTEA NOT NULL DEFAULT '',"
182               " block BYTEA NOT NULL DEFAULT '',"
183               " expiration_time BIGINT NOT NULL DEFAULT 0"
184               ")" "WITH OIDS");
185   }
186   if ( (NULL == res) ||
187        ((PQresultStatus (res) != PGRES_COMMAND_OK) &&
188         (0 != strcmp ("42P07",    /* duplicate table */
189                       PQresultErrorField
190                       (res,
191                        PG_DIAG_SQLSTATE)))))
192   {
193     (void) GNUNET_POSTGRES_check_result (plugin->dbh, res,
194                                          PGRES_COMMAND_OK, "CREATE TABLE",
195                                          "ns096blocks");
196     PQfinish (plugin->dbh);
197     plugin->dbh = NULL;
198     return GNUNET_SYSERR;
199   }
200   if (PQresultStatus (res) == PGRES_COMMAND_OK)
201     create_indices (plugin->dbh);
202   PQclear (res);
203
204   if ((GNUNET_OK !=
205        GNUNET_POSTGRES_prepare (plugin->dbh,
206                                 "cache_block",
207                                 "INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
208                                 "($1, $2, $3)", 3)) ||
209       (GNUNET_OK !=
210        GNUNET_POSTGRES_prepare (plugin->dbh,
211                                 "expire_blocks",
212                                 "DELETE FROM ns096blocks WHERE expiration_time<$1", 1)) ||
213       (GNUNET_OK !=
214        GNUNET_POSTGRES_prepare (plugin->dbh,
215                                 "delete_block",
216                                 "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2)) ||
217       (GNUNET_OK !=
218        GNUNET_POSTGRES_prepare (plugin->dbh,
219                                 "lookup_block",
220                                 "SELECT block FROM ns096blocks WHERE query=$1"
221                                 " ORDER BY expiration_time DESC LIMIT 1", 1)) ||
222       (GNUNET_OK !=
223        GNUNET_POSTGRES_prepare (plugin->dbh,
224                                 "store_records",
225                                 "INSERT INTO ns097records (zone_private_key, pkey, rvalue, record_count, record_data, label) VALUES "
226                                 "($1, $2, $3, $4, $5, $6)", 6)) ||
227       (GNUNET_OK !=
228        GNUNET_POSTGRES_prepare (plugin->dbh,
229                                 "delete_records",
230                                 "DELETE FROM ns097records WHERE zone_private_key=$1 AND label=$2", 2)) ||
231       (GNUNET_OK !=
232        GNUNET_POSTGRES_prepare (plugin->dbh,
233                                 "zone_to_name",
234                                 "SELECT record_count,record_data,label FROM ns097records"
235                                 " WHERE zone_private_key=$1 AND pkey=$2", 2)) ||
236       (GNUNET_OK !=
237        GNUNET_POSTGRES_prepare (plugin->dbh,
238                                 "iterate_zone",
239                                 "SELECT record_count, record_data, label FROM ns097records"
240                                 " WHERE zone_private_key=$1 ORDER BY rvalue LIMIT 1 OFFSET $2", 2)) ||
241       (GNUNET_OK !=
242        GNUNET_POSTGRES_prepare (plugin->dbh,
243                                 "iterate_all_zones",
244                                 "SELECT record_count,record_data,label,zone_private_key"
245                                 " FROM ns097records ORDER BY rvalue LIMIT 1 OFFSET $1", 1)))
246   {
247     PQfinish (plugin->dbh);
248     plugin->dbh = NULL;
249     return GNUNET_SYSERR;
250   }
251   return GNUNET_OK;
252 }
253
254
255 /**
256  * Removes any expired block.
257  *
258  * @param plugin the plugin
259  */
260 static void
261 namestore_postgres_expire_blocks (struct Plugin *plugin)
262 {
263   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
264   struct GNUNET_TIME_AbsoluteNBO now_be = GNUNET_TIME_absolute_hton (now);
265   const char *paramValues[] = {
266     (const char *) &now_be
267   };
268   int paramLengths[] = {
269     sizeof (now_be)
270   };
271   const int paramFormats[] = { 1 };
272   PGresult *res;
273
274   res =
275     PQexecPrepared (plugin->dbh, "expire_blocks", 1,
276                     paramValues, paramLengths, paramFormats, 1);
277   if (GNUNET_OK !=
278       GNUNET_POSTGRES_check_result (plugin->dbh,
279                                     res,
280                                     PGRES_COMMAND_OK,
281                                     "PQexecPrepared",
282                                     "expire_blocks"))
283     return;
284   PQclear (res);
285 }
286
287
288 /**
289  * Delete older block in the datastore.
290  *
291  * @param the plugin
292  * @param query query for the block
293  * @param expiration time how old does the block have to be for deletion
294  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
295  */
296 static void
297 delete_old_block (struct Plugin *plugin,
298                   const struct GNUNET_HashCode *query,
299                   struct GNUNET_TIME_AbsoluteNBO expiration_time)
300 {
301   const char *paramValues[] = {
302     (const char *) query,
303     (const char *) &expiration_time
304   };
305   int paramLengths[] = {
306     sizeof (*query),
307     sizeof (expiration_time)
308   };
309   const int paramFormats[] = { 1, 1 };
310   PGresult *res;
311
312   res =
313     PQexecPrepared (plugin->dbh, "delete_block", 2,
314                     paramValues, paramLengths, paramFormats, 1);
315   if (GNUNET_OK !=
316       GNUNET_POSTGRES_check_result (plugin->dbh,
317                                     res,
318                                     PGRES_COMMAND_OK,
319                                     "PQexecPrepared",
320                                     "delete_block"))
321     return;
322   PQclear (res);
323 }
324
325
326 /**
327  * Cache a block in the datastore.
328  *
329  * @param cls closure (internal context for the plugin)
330  * @param block block to cache
331  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
332  */
333 static int
334 namestore_postgres_cache_block (void *cls,
335                                 const struct GNUNET_NAMESTORE_Block *block)
336 {
337   struct Plugin *plugin = cls;
338   struct GNUNET_HashCode query;
339   size_t block_size = ntohl (block->purpose.size) +
340     sizeof (struct GNUNET_CRYPTO_EccPublicSignKey) +
341     sizeof (struct GNUNET_CRYPTO_EccSignature);
342   const char *paramValues[] = {
343     (const char *) &query,
344     (const char *) block,
345     (const char *) &block->expiration_time
346   };
347   int paramLengths[] = {
348     sizeof (query),
349     (int) block_size,
350     sizeof (block->expiration_time)
351   };
352   const int paramFormats[] = { 1, 1, 1 };
353   PGresult *res;
354
355   namestore_postgres_expire_blocks (plugin);
356   GNUNET_CRYPTO_hash (&block->derived_key,
357                       sizeof (struct GNUNET_CRYPTO_EccPublicSignKey),
358                       &query);
359   if (block_size > 64 * 65536)
360   {
361     GNUNET_break (0);
362     return GNUNET_SYSERR;
363   }
364   delete_old_block (plugin, &query, block->expiration_time);
365
366   res =
367     PQexecPrepared (plugin->dbh, "cache_block", 3,
368                     paramValues, paramLengths, paramFormats, 1);
369   if (GNUNET_OK !=
370       GNUNET_POSTGRES_check_result (plugin->dbh,
371                                     res,
372                                     PGRES_COMMAND_OK,
373                                     "PQexecPrepared",
374                                     "cache_block"))
375     return GNUNET_SYSERR;
376   PQclear (res);
377   return GNUNET_OK;
378 }
379
380
381 /**
382  * Get the block for a particular zone and label in the
383  * datastore.  Will return at most one result to the iterator.
384  *
385  * @param cls closure (internal context for the plugin)
386  * @param query hash of public key derived from the zone and the label
387  * @param iter function to call with the result
388  * @param iter_cls closure for @a iter
389  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
390  */
391 static int
392 namestore_postgres_lookup_block (void *cls,
393                                  const struct GNUNET_HashCode *query,
394                                  GNUNET_NAMESTORE_BlockCallback iter, void *iter_cls)
395 {
396   struct Plugin *plugin = cls;
397   const char *paramValues[] = {
398     (const char *) query
399   };
400   int paramLengths[] = {
401     sizeof (*query)
402   };
403   const int paramFormats[] = { 1 };
404   PGresult *res;
405   unsigned int cnt;
406   size_t bsize;
407   const struct GNUNET_NAMESTORE_Block *block;
408
409   res = PQexecPrepared (plugin->dbh,
410                         "lookup_block", 1,
411                         paramValues, paramLengths, paramFormats,
412                         1);
413   if (GNUNET_OK !=
414       GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK,
415                                     "PQexecPrepared",
416                                     "lookup_block"))
417   {
418     LOG (GNUNET_ERROR_TYPE_DEBUG,
419          "Failing lookup (postgres error)\n");
420     return GNUNET_SYSERR;
421   }
422   if (0 == (cnt = PQntuples (res)))
423   {
424     /* no result */
425     LOG (GNUNET_ERROR_TYPE_DEBUG,
426          "Ending iteration (no more results)\n");
427     PQclear (res);
428     return GNUNET_NO;
429   }
430   GNUNET_assert (1 == cnt);
431   GNUNET_assert (1 != PQnfields (res));
432   bsize = PQgetlength (res, 0, 0);
433   block = (const struct GNUNET_NAMESTORE_Block *) PQgetvalue (res, 0, 0);
434   if ( (bsize < sizeof (*block)) ||
435        (bsize != ntohl (block->purpose.size) +
436         sizeof (struct GNUNET_CRYPTO_EccPublicSignKey) +
437         sizeof (struct GNUNET_CRYPTO_EccSignature)) )
438   {
439     GNUNET_break (0);
440     LOG (GNUNET_ERROR_TYPE_DEBUG,
441          "Failing lookup (corrupt block)\n");
442     PQclear (res);
443     return GNUNET_SYSERR;
444   }
445   iter (iter_cls, block);
446   PQclear (res);
447   return GNUNET_OK;
448 }
449
450
451 /**
452  * Store a record in the datastore.  Removes any existing record in the
453  * same zone with the same name.
454  *
455  * @param cls closure (internal context for the plugin)
456  * @param zone_key private key of the zone
457  * @param label name that is being mapped (at most 255 characters long)
458  * @param rd_count number of entries in @a rd array
459  * @param rd array of records with data to store
460  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
461  */
462 static int
463 namestore_postgres_store_records (void *cls,
464                                   const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
465                                   const char *label,
466                                   unsigned int rd_count,
467                                   const struct GNUNET_NAMESTORE_RecordData *rd)
468 {
469   struct Plugin *plugin = cls;
470   struct GNUNET_CRYPTO_EccPublicSignKey pkey;
471   uint64_t rvalue;
472   uint32_t rd_count_nbo = htonl ((uint32_t) rd_count);
473   size_t data_size;
474   unsigned int i;
475
476   memset (&pkey, 0, sizeof (pkey));
477   for (i=0;i<rd_count;i++)
478     if (GNUNET_NAMESTORE_TYPE_PKEY == rd[i].record_type)
479     {
480       GNUNET_break (sizeof (struct GNUNET_CRYPTO_EccPublicSignKey) == rd[i].data_size);
481       memcpy (&pkey,
482               rd[i].data,
483               rd[i].data_size);
484       break;
485     }
486   rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
487   data_size = GNUNET_NAMESTORE_records_get_size (rd_count, rd);
488   if (data_size > 64 * 65536)
489   {
490     GNUNET_break (0);
491     return GNUNET_SYSERR;
492   }
493   {
494     char data[data_size];
495     const char *paramValues[] = {
496       (const char *) zone_key,
497       (const char *) &pkey,
498       (const char *) &rvalue,
499       (const char *) &rd_count_nbo,
500       (const char *) data,
501       label
502     };
503     int paramLengths[] = {
504       sizeof (*zone_key),
505       sizeof (pkey),
506       sizeof (rvalue),
507       sizeof (rd_count_nbo),
508       data_size,
509       strlen (label)
510     };
511     const int paramFormats[] = { 1, 1, 1, 1, 1, 1 };
512     PGresult *res;
513
514     if (data_size != GNUNET_NAMESTORE_records_serialize (rd_count, rd,
515                                                          data_size, data))
516     {
517       GNUNET_break (0);
518       return GNUNET_SYSERR;
519     }
520
521     res =
522       PQexecPrepared (plugin->dbh, "store_records", 6,
523                       paramValues, paramLengths, paramFormats, 1);
524     if (GNUNET_OK !=
525         GNUNET_POSTGRES_check_result (plugin->dbh,
526                                       res,
527                                       PGRES_COMMAND_OK,
528                                       "PQexecPrepared",
529                                       "store_records"))
530       return GNUNET_SYSERR;
531     PQclear (res);
532     return GNUNET_OK;
533   }
534 }
535
536
537 /**
538  * A statement has been run.  We should evaluate the result, and if possible
539  * call the given @a iter with the result.
540  *
541  * @param plugin plugin context
542  * @param res result from the statement that was run (to be cleaned up)
543  * @param zone_key private key of the zone, could be NULL, in which case we should
544  *        get the zone from @a res
545  * @param iter iterator to call with the result
546  * @param iter_cls closure for @a iter
547  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
548  */
549 static int
550 get_record_and_call_iterator (struct Plugin *plugin,
551                               PGresult *res,
552                               const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
553                               GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
554 {
555   const char *data;
556   size_t data_size;
557   uint32_t record_count;
558   const char *label;
559   size_t label_len;
560   unsigned int cnt;
561
562   if (GNUNET_OK !=
563       GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK,
564                                     "PQexecPrepared",
565                                     "iteration"))
566   {
567     LOG (GNUNET_ERROR_TYPE_DEBUG,
568          "Failing lookup (postgres error)\n");
569     return GNUNET_SYSERR;
570   }
571   if (0 == (cnt = PQntuples (res)))
572   {
573     /* no result */
574     LOG (GNUNET_ERROR_TYPE_DEBUG,
575          "Ending iteration (no more results)\n");
576     PQclear (res);
577     return GNUNET_NO;
578   }
579   GNUNET_assert (1 == cnt);
580   GNUNET_assert (3 + ((NULL == zone_key) ? 1 : 0) == PQnfields (res));
581   if (NULL == zone_key)
582   {
583     if (sizeof (struct GNUNET_CRYPTO_EccPrivateKey) != PQgetlength (res, 0, 3))
584     {
585       GNUNET_break (0);
586       PQclear (res);
587       return GNUNET_SYSERR;
588     }
589     zone_key = (const struct GNUNET_CRYPTO_EccPrivateKey *) PQgetvalue (res, 0, 3);
590   }
591   if (sizeof (uint32_t) != PQfsize (res, 0))
592   {
593     GNUNET_break (0);
594     PQclear (res);
595     return GNUNET_SYSERR;
596   }
597
598   record_count = ntohl (*(uint32_t *) PQgetvalue (res, 0, 0));
599   data = PQgetvalue (res, 0, 1);
600   data_size = PQgetlength (res, 0, 1);
601   label = PQgetvalue (res, 0, 2);
602   label_len = PQgetlength (res, 0, 1);
603   if (record_count > 64 * 1024)
604   {
605     /* sanity check, don't stack allocate far too much just
606        because database might contain a large value here */
607     GNUNET_break (0);
608     PQclear (res);
609     return GNUNET_SYSERR;
610   }
611   {
612     struct GNUNET_NAMESTORE_RecordData rd[record_count];
613     char buf[label_len + 1];
614
615     memcpy (buf, label, label_len);
616     buf[label_len] = '\0';
617     if (GNUNET_OK !=
618         GNUNET_NAMESTORE_records_deserialize (data_size, data,
619                                               record_count, rd))
620     {
621       GNUNET_break (0);
622       PQclear (res);
623       return GNUNET_SYSERR;
624     }
625     iter (iter_cls, zone_key, buf, record_count, rd);
626   }
627   PQclear (res);
628   return GNUNET_OK;
629 }
630
631
632 /**
633  * Iterate over the results for a particular key and zone in the
634  * datastore.  Will return at most one result to the iterator.
635  *
636  * @param cls closure (internal context for the plugin)
637  * @param zone hash of public key of the zone, NULL to iterate over all zones
638  * @param offset offset in the list of all matching records
639  * @param iter function to call with the result
640  * @param iter_cls closure for @a iter
641  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
642  */
643 static int
644 namestore_postgres_iterate_records (void *cls,
645                                     const struct GNUNET_CRYPTO_EccPrivateKey *zone,
646                                     uint64_t offset,
647                                     GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
648 {
649   struct Plugin *plugin = cls;
650   uint64_t offset_be = GNUNET_htonll (offset);
651
652   if (NULL == zone)
653   {
654     const char *paramValues[] = {
655       (const char *) &offset_be
656     };
657     int paramLengths[] = {
658       sizeof (offset_be)
659     };
660     const int paramFormats[] = { 1 };
661     PGresult *res;
662
663     res = PQexecPrepared (plugin->dbh,
664                           "iterate_all_zones", 1,
665                           paramValues, paramLengths, paramFormats,
666                           1);
667     return get_record_and_call_iterator (plugin,
668                                          res,
669                                          NULL,
670                                          iter, iter_cls);
671   }
672   else
673   {
674     const char *paramValues[] = {
675       (const char *) zone,
676       (const char *) &offset_be
677     };
678     int paramLengths[] = {
679       sizeof (*zone),
680       sizeof (offset_be)
681     };
682     const int paramFormats[] = { 1, 1 };
683     PGresult *res;
684
685     res = PQexecPrepared (plugin->dbh,
686                           "iterate_zone", 2,
687                           paramValues, paramLengths, paramFormats,
688                           1);
689     return get_record_and_call_iterator (plugin,
690                                          res,
691                                          zone,
692                                          iter, iter_cls);
693   }
694 }
695
696
697 /**
698  * Look for an existing PKEY delegation record for a given public key.
699  * Returns at most one result to the iterator.
700  *
701  * @param cls closure (internal context for the plugin)
702  * @param zone private key of the zone to look up in, never NULL
703  * @param value_zone public key of the target zone (value), never NULL
704  * @param iter function to call with the result
705  * @param iter_cls closure for @a iter
706  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
707  */
708 static int
709 namestore_postgres_zone_to_name (void *cls,
710                                  const struct GNUNET_CRYPTO_EccPrivateKey *zone,
711                                  const struct GNUNET_CRYPTO_EccPublicSignKey *value_zone,
712                                  GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
713 {
714   struct Plugin *plugin = cls;
715   const char *paramValues[] = {
716     (const char *) zone,
717     (const char *) value_zone
718   };
719   int paramLengths[] = {
720     sizeof (*zone),
721     sizeof (*value_zone)
722   };
723   const int paramFormats[] = { 1, 1 };
724   PGresult *res;
725
726   res = PQexecPrepared (plugin->dbh,
727                         "zone_to_name", 2,
728                         paramValues, paramLengths, paramFormats,
729                         1);
730   return get_record_and_call_iterator (plugin,
731                                        res,
732                                        zone,
733                                        iter, iter_cls);
734 }
735
736
737 /**
738  * Shutdown database connection and associate data
739  * structures.
740  *
741  * @param plugin the plugin context (state for this module)
742  */
743 static void
744 database_shutdown (struct Plugin *plugin)
745 {
746   PQfinish (plugin->dbh);
747   plugin->dbh = NULL;
748 }
749
750
751 /**
752  * Entry point for the plugin.
753  *
754  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
755  * @return NULL on error, othrewise the plugin context
756  */
757 void *
758 libgnunet_plugin_namestore_postgres_init (void *cls)
759 {
760   static struct Plugin plugin;
761   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
762   struct GNUNET_NAMESTORE_PluginFunctions *api;
763
764   if (NULL != plugin.cfg)
765     return NULL;                /* can only initialize once! */
766   memset (&plugin, 0, sizeof (struct Plugin));
767   plugin.cfg = cfg;
768   if (GNUNET_OK != database_setup (&plugin))
769   {
770     database_shutdown (&plugin);
771     return NULL;
772   }
773   api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions);
774   api->cls = &plugin;
775   api->cache_block = &namestore_postgres_cache_block;
776   api->lookup_block = &namestore_postgres_lookup_block;
777   api->store_records = &namestore_postgres_store_records;
778   api->iterate_records = &namestore_postgres_iterate_records;
779   api->zone_to_name = &namestore_postgres_zone_to_name;
780   LOG (GNUNET_ERROR_TYPE_INFO,
781        _("Postgres database running\n"));
782   return api;
783 }
784
785
786 /**
787  * Exit point from the plugin.
788  *
789  * @param cls the plugin context (as returned by "init")
790  * @return always NULL
791  */
792 void *
793 libgnunet_plugin_namestore_postgres_done (void *cls)
794 {
795   struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
796   struct Plugin *plugin = api->cls;
797
798   database_shutdown (plugin);
799   plugin->cfg = NULL;
800   GNUNET_free (api);
801   LOG (GNUNET_ERROR_TYPE_DEBUG,
802        "postgres plugin is finished\n");
803   return NULL;
804 }
805
806 /* end of plugin_namestore_postgres.c */