fix: fail to compile
[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
27 #include "platform.h"
28 #include "gnunet_namestore_plugin.h"
29 #include "gnunet_namestore_service.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, "CREATE INDEX ir_zone_name_rv ON ns091records (zone_hash,record_name_hash,rvalue)")) ||
84        (GNUNET_OK !=
85         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX ir_zone_delegation ON ns091records (zone_hash,zone_delegation)")) ||
86        (GNUNET_OK !=
87         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX ir_zone_rv ON ns091records (zone_hash,rvalue)")) ||
88        (GNUNET_OK !=
89         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX ir_zone ON ns091records (zone_hash)")) ||
90        (GNUNET_OK !=
91         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX ir_name_rv ON ns091records (record_name_hash,rvalue)")) ||
92        (GNUNET_OK !=
93         GNUNET_POSTGRES_exec (dbh, "CREATE INDEX ir_rv ON ns091records (rvalue)")) )
94     LOG (GNUNET_ERROR_TYPE_ERROR, 
95          _("Failed to create indices\n"));
96 }
97
98
99 /**
100  * Initialize the database connections and associated
101  * data structures (create tables and indices
102  * as needed as well).
103  *
104  * @param plugin the plugin context (state for this module)
105  * @return GNUNET_OK on success
106  */
107 static int
108 database_setup (struct Plugin *plugin)
109 {
110   PGresult *res;
111
112   plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg,
113                                          "namestore-postgres");
114   if (NULL == plugin->dbh)
115     return GNUNET_SYSERR;
116   if (GNUNET_YES == 
117       GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
118                                             "namestore-postgres",
119                                             "TEMPORARY_TABLE"))
120   {
121     res =
122       PQexec (plugin->dbh,
123               "CREATE TEMPORARY TABLE ns091records ("
124               " zone_key BYTEA NOT NULL DEFAULT ''," 
125               " zone_delegation BYTEA NOT NULL DEFAULT ''," 
126               " zone_hash BYTEA NOT NULL DEFAULT ''," 
127               " record_count INTEGER NOT NULL DEFAULT 0,"
128               " record_data BYTEA NOT NULL DEFAULT '',"
129               " block_expiration_time BIGINT NOT NULL DEFAULT 0," 
130               " signature BYTEA NOT NULL DEFAULT '',"
131               " record_name TEXT NOT NULL DEFAULT ''," 
132               " record_name_hash BYTEA NOT NULL DEFAULT ''," 
133               " rvalue BIGINT NOT NULL DEFAULT 0"
134               ")" "WITH OIDS");
135   }
136   else
137     res =
138       PQexec (plugin->dbh,
139               "CREATE TABLE ns091records ("
140               " zone_key BYTEA NOT NULL DEFAULT ''," 
141               " zone_delegation BYTEA NOT NULL DEFAULT ''," 
142               " zone_hash BYTEA NOT NULL DEFAULT ''," 
143               " record_count INTEGER NOT NULL DEFAULT 0,"
144               " record_data BYTEA NOT NULL DEFAULT '',"
145               " block_expiration_time BIGINT NOT NULL DEFAULT 0," 
146               " signature BYTEA NOT NULL DEFAULT '',"
147               " record_name TEXT NOT NULL DEFAULT ''," 
148               " record_name_hash BYTEA NOT NULL DEFAULT ''," 
149               " rvalue BIGINT NOT NULL DEFAULT 0"
150               ")" "WITH OIDS");
151
152   if ((NULL == res) || ((PQresultStatus (res) != PGRES_COMMAND_OK) && (0 != strcmp ("42P07",    /* duplicate table */
153                                                                                     PQresultErrorField
154                                                                                     (res,
155                                                                                      PG_DIAG_SQLSTATE)))))
156   {
157     (void) GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_COMMAND_OK, "CREATE TABLE",
158                                          "ns091records");
159     PQfinish (plugin->dbh);
160     plugin->dbh = NULL;
161     return GNUNET_SYSERR;
162   }
163   if (PQresultStatus (res) == PGRES_COMMAND_OK)
164     create_indices (plugin->dbh);
165   PQclear (res);
166
167   if ((GNUNET_OK !=
168        GNUNET_POSTGRES_prepare (plugin->dbh,
169                                 "put_records",
170                                 "INSERT INTO ns091records (zone_key, record_name, record_count, record_data, block_expiration_time, signature" 
171                                 ", zone_delegation, zone_hash, record_name_hash, rvalue) VALUES "
172                                 "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", 10)) ||
173       (GNUNET_OK !=
174        GNUNET_POSTGRES_prepare (plugin->dbh,
175                                 "remove_records",
176                                 "DELETE FROM ns091records WHERE zone_hash=$1 AND record_name_hash=$2", 2)) ||
177       (GNUNET_OK !=
178        GNUNET_POSTGRES_prepare (plugin->dbh,
179                                 "iterate_records",
180                                 "SELECT zone_key, record_name, record_count, record_data, block_expiration_time, signature" 
181                                 " FROM ns091records WHERE zone_hash=$1 AND record_name_hash=$2 ORDER BY rvalue LIMIT 1 OFFSET $3", 3)) ||
182       (GNUNET_OK !=
183        GNUNET_POSTGRES_prepare (plugin->dbh,
184                                 "iterate_by_zone",
185                                 "SELECT zone_key, record_name, record_count, record_data, block_expiration_time, signature" 
186                                 " FROM ns091records WHERE zone_hash=$1 ORDER BY rvalue  LIMIT 1 OFFSET $2", 2)) ||
187       (GNUNET_OK !=
188        GNUNET_POSTGRES_prepare (plugin->dbh,
189                                 "iterate_by_name",
190                                 "SELECT zone_key, record_name, record_count, record_data, block_expiration_time, signature" 
191                                 " FROM ns091records WHERE record_name_hash=$1 ORDER BY rvalue LIMIT 1 OFFSET $2", 2)) ||
192       (GNUNET_OK !=
193        GNUNET_POSTGRES_prepare (plugin->dbh,
194                                 "iterate_all",
195                                 "SELECT zone_key, record_name, record_count, record_data, block_expiration_time, signature" 
196                                 " FROM ns091records ORDER BY rvalue LIMIT 1 OFFSET $1", 1)) ||
197       (GNUNET_OK !=
198        GNUNET_POSTGRES_prepare (plugin->dbh,
199                                 "zone_to_name",
200                                 "SELECT zone_key, record_name, record_count, record_data, block_expiration_time, signature"
201                                 " FROM ns091records WHERE zone_hash=$1 AND zone_delegation=$2", 2)) ||
202       (GNUNET_OK !=
203        GNUNET_POSTGRES_prepare (plugin->dbh,
204                                 "delete_zone",
205                                 "DELETE FROM ns091records WHERE zone_hash=$1", 1)))
206   {
207     PQfinish (plugin->dbh);
208     plugin->dbh = NULL;
209     return GNUNET_SYSERR;
210   }
211   return GNUNET_OK;
212 }
213
214 #if 0
215
216 TODO: removed
217
218 /**
219  * Removes any existing record in the given zone with the same name.
220  *
221  * @param cls closure (internal context for the plugin)
222  * @param zone hash of the public key of the zone
223  * @param name name to remove (at most 255 characters long)
224  * @return GNUNET_OK on success
225  */
226 static int 
227 namestore_postgres_remove_records (void *cls, 
228                                  const struct GNUNET_CRYPTO_ShortHashCode *zone,
229                                  const char *name)
230 {
231   struct Plugin *plugin = cls;
232   PGresult *ret;
233   struct GNUNET_CRYPTO_ShortHashCode nh;
234   const char *paramValues[] = {
235     (const char *) zone,
236     (const char *) &nh
237   };
238   int paramLengths[] = {
239     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
240     sizeof (struct GNUNET_CRYPTO_ShortHashCode)
241   };
242   const int paramFormats[] = { 1, 1 };
243   size_t name_len;
244
245   name_len = strlen (name);
246   GNUNET_CRYPTO_short_hash (name, name_len, &nh);
247   ret =
248       PQexecPrepared (plugin->dbh, "remove_records", 2, paramValues, paramLengths,
249                       paramFormats, 1);
250   if (GNUNET_OK !=
251       GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "remove_records"))
252     return GNUNET_SYSERR;
253   PQclear (ret);
254   return GNUNET_OK;
255 }
256
257
258
259 /**
260  * Store a record in the datastore.  Removes any existing record in the
261  * same zone with the same name.
262  *
263  * @param cls closure (internal context for the plugin)
264  * @param zone_key public key of the zone
265  * @param expire when does the corresponding block in the DHT expire (until
266  *               when should we never do a DHT lookup for the same name again)?
267  * @param name name that is being mapped (at most 255 characters long)
268  * @param rd_count number of entries in 'rd' array
269  * @param rd array of records with data to store
270  * @param signature signature of the record block, NULL if signature is unavailable (i.e. 
271  *        because the user queried for a particular record type only)
272  * @return GNUNET_OK on success, else GNUNET_SYSERR
273  */
274 static int 
275 namestore_postgres_put_records (void *cls, 
276                                 const struct GNUNET_CRYPTO_EccPublicSignKey *zone_key,
277                                 struct GNUNET_TIME_Absolute expire,
278                                 const char *name,
279                                 unsigned int rd_count,
280                                 const struct GNUNET_NAMESTORE_RecordData *rd,
281                                 const struct GNUNET_CRYPTO_EccSignature *signature)
282 {
283   struct Plugin *plugin = cls;
284   PGresult *ret;
285   struct GNUNET_CRYPTO_ShortHashCode zone;
286   struct GNUNET_CRYPTO_ShortHashCode zone_delegation;
287   struct GNUNET_CRYPTO_ShortHashCode nh;
288   size_t name_len;
289   uint64_t rvalue;
290   size_t data_size;
291   unsigned int i;
292
293   GNUNET_CRYPTO_short_hash (zone_key, 
294                             sizeof (struct GNUNET_CRYPTO_EccPublicSignKey),
295                             &zone);
296   (void) namestore_postgres_remove_records (plugin, &zone, name);
297   name_len = strlen (name);
298   GNUNET_CRYPTO_short_hash (name, name_len, &nh);
299   memset (&zone_delegation, 0, sizeof (zone_delegation));
300   for (i=0;i<rd_count;i++)
301     if (rd[i].record_type == GNUNET_NAMESTORE_TYPE_PKEY)
302     {
303       GNUNET_assert (sizeof (struct GNUNET_CRYPTO_ShortHashCode) == rd[i].data_size);
304       memcpy (&zone_delegation,
305               rd[i].data,
306               sizeof (struct GNUNET_CRYPTO_ShortHashCode));
307       break;
308     }
309   rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
310   data_size = GNUNET_NAMESTORE_records_get_size (rd_count, rd);
311   if (data_size > 64 * 65536)
312   {
313     GNUNET_break (0);
314     return GNUNET_SYSERR;
315   }
316   {
317     char data[data_size];
318     uint64_t expire_be = GNUNET_htonll (expire.abs_value_us);
319     uint64_t rvalue_be = GNUNET_htonll (rvalue);
320     uint32_t rd_count_be = htonl ((uint32_t) rd_count);
321     const char *paramValues[] = {
322       (const char *) zone_key,
323       (const char *) name,
324       (const char *) &rd_count_be,
325       (const char *) data,
326       (const char *) &expire_be,
327       (const char *) signature,
328       (const char *) &zone_delegation,
329       (const char *) &zone,
330       (const char *) &nh,
331       (const char *) &rvalue_be
332     };
333     int paramLengths[] = {
334       sizeof (struct GNUNET_CRYPTO_EccPublicSignKey),
335       name_len,
336       sizeof (uint32_t),
337       data_size,
338       sizeof (uint64_t),
339       sizeof (struct GNUNET_CRYPTO_EccSignature), 
340       sizeof (struct GNUNET_CRYPTO_ShortHashCode),
341       sizeof (struct GNUNET_CRYPTO_ShortHashCode),
342       sizeof (struct GNUNET_CRYPTO_ShortHashCode),
343       sizeof (uint64_t)
344     };
345     const int paramFormats[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
346
347     if (data_size != GNUNET_NAMESTORE_records_serialize (rd_count, rd,
348                                                          data_size, data))
349     {
350       GNUNET_break (0);
351       return GNUNET_SYSERR;
352     }
353     ret =
354       PQexecPrepared (plugin->dbh, "put_records", 10, paramValues, paramLengths,
355                       paramFormats, 1);
356     if (GNUNET_OK !=
357         GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "put_records"))
358       return GNUNET_SYSERR;
359     PQclear (ret);
360   }
361   return GNUNET_OK;  
362 }
363 #endif
364
365 #if 0
366 /**
367  * The given 'postgres' result was obtained from the database.
368  * Parse the record and give it to the iterator.
369  *
370  * @param plugin plugin context
371  * @param stmt_name name of the prepared statement that was executed
372  * @param res result from postgres to interpret (and then clean up)
373  * @param iter iterator to call with the result
374  * @param iter_cls closure for 'iter'
375  * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error
376  *       'iter' will have been called unless the return value is 'GNUNET_SYSERR'
377  */
378 static int
379 get_record_and_call_iterator (struct Plugin *plugin,
380                               const char *stmt_name,
381                               PGresult *res,
382                               GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
383 {
384   unsigned int record_count;
385   size_t data_size;
386   /* const struct GNUNET_CRYPTO_EccPublicSignKey *zone_key; */
387   const struct GNUNET_CRYPTO_EccPrivateKey *zone_key;
388   /* const struct GNUNET_CRYPTO_EccSignature *sig; */
389   /* struct GNUNET_TIME_Absolute expiration; */
390   const char *data;
391   const char *name;
392   unsigned int cnt;
393   size_t name_len;
394
395   if (GNUNET_OK !=
396       GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK, "PQexecPrepared",
397                                     stmt_name))
398   {
399     LOG (GNUNET_ERROR_TYPE_DEBUG,
400          "Ending iteration (postgres error)\n");
401     return GNUNET_SYSERR;
402   }
403
404   if (0 == (cnt = PQntuples (res)))
405   {
406     /* no result */
407     LOG (GNUNET_ERROR_TYPE_DEBUG, 
408          "Ending iteration (no more results)\n");
409     PQclear (res);
410
411     iter (iter_cls, NULL, NULL, 0, NULL);
412     return GNUNET_NO;
413   }
414   GNUNET_assert (1 == cnt);
415   if ((6 != PQnfields (res)) || 
416       (sizeof (struct GNUNET_CRYPTO_EccPublicSignKey) != PQgetlength (res, 0, 0)) || 
417       (sizeof (uint32_t) != PQfsize (res, 2)) || 
418       (sizeof (uint64_t) != PQfsize (res, 4)) || 
419       (sizeof (struct GNUNET_CRYPTO_EccSignature) != PQgetlength (res, 0, 5)))
420   {
421     GNUNET_break (0);
422     PQclear (res);
423     return GNUNET_SYSERR;
424   }
425   zone_key = (const struct GNUNET_CRYPTO_EccPrivateKey *) PQgetvalue (res, 0, 0);
426   /* zone_key = (const struct GNUNET_CRYPTO_EccPublicSignKey *) PQgetvalue (res, 0, 0); */
427   name = PQgetvalue (res, 0, 1);
428   name_len = PQgetlength (res, 0, 1);
429   record_count = ntohl (*(uint32_t *) PQgetvalue (res, 0, 2));
430   data_size = PQgetlength (res, 0, 3);
431   data = PQgetvalue (res, 0, 3);
432   /*
433   expiration.abs_value_us =
434     GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, 0, 4));
435   sig = (const struct GNUNET_CRYPTO_EccSignature*) PQgetvalue (res, 0, 5); */
436   if (record_count > 64 * 1024)
437   {
438     /* sanity check, don't stack allocate far too much just
439        because database might contain a large value here */
440     GNUNET_break (0);
441     PQclear (res);
442     return GNUNET_SYSERR;
443   }
444   {
445     struct GNUNET_NAMESTORE_RecordData rd[record_count];
446     char buf[name_len + 1];
447     
448     memcpy (buf, name, name_len);
449     buf[name_len] = '\0';
450     if (GNUNET_OK !=
451         GNUNET_NAMESTORE_records_deserialize (data_size, data,
452                                               record_count, rd))
453     {
454       GNUNET_break (0);
455       PQclear (res);
456       return GNUNET_SYSERR;
457     }
458     iter (iter_cls, zone_key, buf, record_count, rd);
459   }
460   PQclear (res);
461   return GNUNET_OK;
462 }
463 #endif
464   
465 /**
466  * Iterate over the results for a particular key and zone in the
467  * datastore.  Will return at most one result to the iterator.
468  *
469  * @param cls closure (internal context for the plugin)
470  * @param zone hash of public key of the zone, NULL to iterate over all zones
471  * @param offset offset in the list of all matching records
472  * @param iter function to call with the result
473  * @param iter_cls closure for iter
474  * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error
475  *       'iter' will have been called unless the return value is 'GNUNET_SYSERR'
476  */
477 static int 
478 namestore_postgres_iterate_records (void *cls, 
479                                         const struct GNUNET_CRYPTO_EccPrivateKey *zone,
480                                   uint64_t offset,
481                                   GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
482 {
483 #if 0
484   struct Plugin *plugin = cls;
485   const char *stmt_name;
486   struct GNUNET_CRYPTO_ShortHashCode name_hase;
487   uint64_t offset_be = GNUNET_htonll (offset);
488   const char *paramValues[] = {
489     (const char *) zone,
490     (const char *) &name_hase,
491     (const char *) &offset_be,
492     (const char *) zone,
493     (const char *) &offset_be,
494   };
495   int paramLengths[] = {
496     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
497     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
498     sizeof (uint64_t),
499     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
500     sizeof (uint64_t)
501   };
502   const int paramFormats[] = { 1, 1, 1, 1, 1 };
503   unsigned int num_params;
504   unsigned int first_param;
505   PGresult *res;
506
507   if (NULL == zone)
508     if (NULL == name)
509     {
510       stmt_name = "iterate_all";
511       num_params = 1;
512       first_param = 2;
513     }
514     else
515     {
516         /* TODO Adapt to new API
517          * GNUNET_CRYPTO_short_hash (name, strlen(name), &name_hase);
518          */
519       stmt_name = "iterate_by_name";
520       num_params = 2;
521       first_param = 1;
522     }
523   else
524     if (NULL == name)
525     {
526       stmt_name = "iterate_by_zone";
527       num_params = 2;
528       first_param = 3;
529     }
530     else
531     {
532         /* TODO Adapt to new API
533          * GNUNET_CRYPTO_short_hash (name, strlen(name), &name_hase);
534          */
535       stmt_name = "iterate_records";
536       num_params = 3;
537       first_param = 0;
538     }
539   res =
540       PQexecPrepared (plugin->dbh, stmt_name, num_params, 
541                       &paramValues[first_param],
542                       &paramLengths[first_param],
543                       &paramFormats[first_param], 1);
544   return get_record_and_call_iterator (plugin, stmt_name, res, iter, iter_cls);
545 #endif
546   GNUNET_break (0);
547   return GNUNET_SYSERR;
548 }
549
550
551 /**
552  * Look for an existing PKEY delegation record for a given public key.
553  * Returns at most one result to the iterator.
554  *
555  * @param cls closure (internal context for the plugin)
556  * @param zone hash of public key of the zone to look up in, never NULL
557  * @param value_zone hash of the public key of the target zone (value), never NULL
558  * @param iter function to call with the result
559  * @param iter_cls closure for iter
560  * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error
561  *       'iter' will have been called unless the return value is 'GNUNET_SYSERR'
562  */
563 static int
564 namestore_postgres_zone_to_name (void *cls, 
565                                const struct GNUNET_CRYPTO_EccPrivateKey *zone,
566                                const struct GNUNET_CRYPTO_EccPublicSignKey *value_zone,
567                                GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
568 {
569 #if  0
570   struct Plugin *plugin = cls;
571   const char *paramValues[] = {
572     (const char *) zone,
573     (const char *) value_zone
574   };
575   int paramLengths[] = {
576     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
577     sizeof (struct GNUNET_CRYPTO_ShortHashCode)
578   };
579   const int paramFormats[] = { 1, 1 };
580   PGresult *res;
581
582   res =
583     PQexecPrepared (plugin->dbh, "zone_to_name", 2,
584                     paramValues, paramLengths, paramFormats, 1);
585   return get_record_and_call_iterator (plugin, "zone_to_name", res, iter, iter_cls);
586 #endif
587   GNUNET_break (0);
588   return GNUNET_SYSERR;
589 }
590
591 #if 0
592 /**
593  * Delete an entire zone (all records).  Not used in normal operation.
594  *
595  * @param cls closure (internal context for the plugin)
596  * @param zone zone to delete
597  */
598 static void 
599 namestore_postgres_delete_zone (void *cls,
600                               const struct GNUNET_CRYPTO_ShortHashCode *zone)
601 {
602   struct Plugin *plugin = cls;
603   PGresult *ret;
604   const char *paramValues[] = {
605     (const char *) zone,
606   };
607
608   int paramLengths[] = {
609     sizeof (struct GNUNET_CRYPTO_ShortHashCode)
610   };
611   const int paramFormats[] = { 1 };
612
613   ret =
614       PQexecPrepared (plugin->dbh, "delete_zone", 1, paramValues, paramLengths,
615                       paramFormats, 1);
616   if (GNUNET_OK !=
617       GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "delete_zone"))
618   {
619     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
620                 "Deleting zone failed!\n");             
621     return;
622   }
623   PQclear (ret);
624 }
625 #endif
626
627 /**
628  * Cache a block in the datastore.
629  *
630  * @param cls closure (internal context for the plugin)
631  * @param block block to cache
632  * @return GNUNET_OK on success, else GNUNET_SYSERR
633  */
634 static int
635 namestore_postgres_cache_block (void *cls,
636                               const struct GNUNET_NAMESTORE_Block *block)
637 {
638   // struct Plugin *plugin = cls;
639   GNUNET_break (0);
640   /* To be implemented */
641   return GNUNET_OK;
642 }
643
644 /**
645  * Get the block for a particular zone and label in the
646  * datastore.  Will return at most one result to the iterator.
647  *
648  * @param cls closure (internal context for the plugin)
649  * @param query hash of public key derived from the zone and the label
650  * @param iter function to call with the result
651  * @param iter_cls closure for iter
652  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
653  */
654 static int
655 namestore_postgres_lookup_block (void *cls,
656                                const struct GNUNET_HashCode *query,
657                                GNUNET_NAMESTORE_BlockCallback iter, void *iter_cls)
658 {
659   // struct Plugin *plugin = cls;
660   GNUNET_break (0);
661   /* To be implemented */
662   return GNUNET_OK;
663 }
664
665 /**
666  * Store a record in the datastore.  Removes any existing record in the
667  * same zone with the same name.
668  *
669  * @param cls closure (internal context for the plugin)
670  * @param zone_key private key of the zone
671  * @param label name that is being mapped (at most 255 characters long)
672  * @param rd_count number of entries in 'rd' array
673  * @param rd array of records with data to store
674  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
675  */
676 static int
677 namestore_postgres_store_records (void *cls,
678                                 const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
679                                 const char *label,
680                                 unsigned int rd_count,
681                                 const struct GNUNET_NAMESTORE_RecordData *rd)
682 {
683   //  struct Plugin *plugin = cls;
684   GNUNET_break (0);
685   /* To be implemented */
686   return GNUNET_OK;
687 }
688
689
690 /**
691  * Shutdown database connection and associate data
692  * structures.
693  *
694  * @param plugin the plugin context (state for this module)
695  */
696 static void
697 database_shutdown (struct Plugin *plugin)
698 {
699   PQfinish (plugin->dbh);
700   plugin->dbh = NULL;
701 }
702
703
704 /**
705  * Entry point for the plugin.
706  *
707  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
708  * @return NULL on error, othrewise the plugin context
709  */
710 void *
711 libgnunet_plugin_namestore_postgres_init (void *cls)
712 {
713   static struct Plugin plugin;
714   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
715   struct GNUNET_NAMESTORE_PluginFunctions *api;
716
717   if (NULL != plugin.cfg)
718     return NULL;                /* can only initialize once! */
719   memset (&plugin, 0, sizeof (struct Plugin));
720   plugin.cfg = cfg;  
721   if (GNUNET_OK != database_setup (&plugin))
722   {
723     database_shutdown (&plugin);
724     return NULL;
725   }
726   api = GNUNET_malloc (sizeof (struct GNUNET_NAMESTORE_PluginFunctions));
727   api->cls = &plugin;
728   /* api->put_records = &namestore_postgres_put_records; */
729   /* api->remove_records = &namestore_postgres_remove_records; */
730   api->cache_block = &namestore_postgres_cache_block;
731   api->lookup_block = &namestore_postgres_lookup_block;
732   api->store_records = &namestore_postgres_store_records;
733   api->iterate_records = &namestore_postgres_iterate_records;
734   api->zone_to_name = &namestore_postgres_zone_to_name;
735   /* api->delete_zone = &namestore_postgres_delete_zone; */
736   LOG (GNUNET_ERROR_TYPE_INFO, 
737        _("Postgres database running\n"));
738   return api;
739 }
740
741
742 /**
743  * Exit point from the plugin.
744  *
745  * @param cls the plugin context (as returned by "init")
746  * @return always NULL
747  */
748 void *
749 libgnunet_plugin_namestore_postgres_done (void *cls)
750 {
751   struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
752   struct Plugin *plugin = api->cls;
753
754   database_shutdown (plugin);
755   plugin->cfg = NULL;
756   GNUNET_free (api);
757   LOG (GNUNET_ERROR_TYPE_DEBUG, 
758        "postgres plugin is finished\n");
759   return NULL;
760 }
761
762 /* end of plugin_namestore_postgres.c */