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