-LRN: initialize rd to avoid having garbage in flags
[oweals/gnunet.git] / src / namestore / plugin_namestore_postgres.c
1  /*
2   * This file is part of GNUnet
3   * (C) 2009, 2011, 2012 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 #define ALL "zone_key, record_name, record_count, record_data, block_expiration_time, signature"
168   if ((GNUNET_OK !=
169        GNUNET_POSTGRES_prepare (plugin->dbh,
170                                 "put_records",
171                                 "INSERT INTO ns091records (" ALL 
172                                 ", zone_delegation, zone_hash, record_name_hash, rvalue) VALUES "
173                                 "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", 10)) ||
174       (GNUNET_OK !=
175        GNUNET_POSTGRES_prepare (plugin->dbh,
176                                 "remove_records",
177                                 "DELETE FROM ns091records WHERE zone_hash=$1 AND record_name_hash=$2", 2)) ||
178       (GNUNET_OK !=
179        GNUNET_POSTGRES_prepare (plugin->dbh,
180                                 "iterate_records",
181                                 "SELECT " ALL
182                                 " FROM ns091records WHERE zone_hash=$1 AND record_name_hash=$2 ORDER BY rvalue LIMIT 1 OFFSET $3", 3)) ||
183       (GNUNET_OK !=
184        GNUNET_POSTGRES_prepare (plugin->dbh,
185                                 "iterate_by_zone",
186                                 "SELECT " ALL
187                                 " FROM ns091records WHERE zone_hash=$1 ORDER BY rvalue  LIMIT 1 OFFSET $2", 2)) ||
188       (GNUNET_OK !=
189        GNUNET_POSTGRES_prepare (plugin->dbh,
190                                 "iterate_by_name",
191                                 "SELECT " ALL 
192                                 " FROM ns091records WHERE record_name_hash=$1 ORDER BY rvalue LIMIT 1 OFFSET $2", 2)) ||
193       (GNUNET_OK !=
194        GNUNET_POSTGRES_prepare (plugin->dbh,
195                                 "iterate_all",
196                                 "SELECT " ALL
197                                 " FROM ns091records ORDER BY rvalue LIMIT 1 OFFSET $1", 1)) ||
198       (GNUNET_OK !=
199        GNUNET_POSTGRES_prepare (plugin->dbh,
200                                 "zone_to_name",
201                                 "SELECT " ALL
202                                 " FROM ns091records WHERE zone_hash=$1 AND zone_delegation=$2", 2)) ||
203       (GNUNET_OK !=
204        GNUNET_POSTGRES_prepare (plugin->dbh,
205                                 "delete_zone",
206                                 "DELETE FROM ns091records WHERE zone_hash=$1", 1)))
207   {
208     PQfinish (plugin->dbh);
209     plugin->dbh = NULL;
210     return GNUNET_SYSERR;
211   }
212 #undef ALL
213   return GNUNET_OK;
214 }
215
216
217 /**
218  * Removes any existing record in the given zone with the same name.
219  *
220  * @param cls closure (internal context for the plugin)
221  * @param zone hash of the public key of the zone
222  * @param name name to remove (at most 255 characters long)
223  * @return GNUNET_OK on success
224  */
225 static int 
226 namestore_postgres_remove_records (void *cls, 
227                                  const struct GNUNET_CRYPTO_ShortHashCode *zone,
228                                  const char *name)
229 {
230   struct Plugin *plugin = cls;
231   PGresult *ret;
232   struct GNUNET_CRYPTO_ShortHashCode nh;
233   const char *paramValues[] = {
234     (const char *) zone,
235     (const char *) &nh
236   };
237   int paramLengths[] = {
238     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
239     sizeof (struct GNUNET_CRYPTO_ShortHashCode)
240   };
241   const int paramFormats[] = { 1, 1 };
242   size_t name_len;
243
244   name_len = strlen (name);
245   GNUNET_CRYPTO_short_hash (name, name_len, &nh);
246   ret =
247       PQexecPrepared (plugin->dbh, "remove_records", 2, paramValues, paramLengths,
248                       paramFormats, 1);
249   if (GNUNET_OK !=
250       GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "remove_records"))
251     return GNUNET_SYSERR;
252   PQclear (ret);
253   return GNUNET_OK;
254 }
255
256
257 /**
258  * Store a record in the datastore.  Removes any existing record in the
259  * same zone with the same name.
260  *
261  * @param cls closure (internal context for the plugin)
262  * @param zone_key public key of the zone
263  * @param expire when does the corresponding block in the DHT expire (until
264  *               when should we never do a DHT lookup for the same name again)?
265  * @param name name that is being mapped (at most 255 characters long)
266  * @param rd_count number of entries in 'rd' array
267  * @param rd array of records with data to store
268  * @param signature signature of the record block, NULL if signature is unavailable (i.e. 
269  *        because the user queried for a particular record type only)
270  * @return GNUNET_OK on success, else GNUNET_SYSERR
271  */
272 static int 
273 namestore_postgres_put_records (void *cls, 
274                                 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
275                                 struct GNUNET_TIME_Absolute expire,
276                                 const char *name,
277                                 unsigned int rd_count,
278                                 const struct GNUNET_NAMESTORE_RecordData *rd,
279                                 const struct GNUNET_CRYPTO_RsaSignature *signature)
280 {
281   struct Plugin *plugin = cls;
282   PGresult *ret;
283   struct GNUNET_CRYPTO_ShortHashCode zone;
284   struct GNUNET_CRYPTO_ShortHashCode zone_delegation;
285   struct GNUNET_CRYPTO_ShortHashCode nh;
286   size_t name_len;
287   uint64_t rvalue;
288   size_t data_size;
289   unsigned int i;
290
291   GNUNET_CRYPTO_short_hash (zone_key, 
292                             sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
293                             &zone);
294   (void) namestore_postgres_remove_records (plugin, &zone, name);
295   name_len = strlen (name);
296   GNUNET_CRYPTO_short_hash (name, name_len, &nh);
297   memset (&zone_delegation, 0, sizeof (zone_delegation));
298   for (i=0;i<rd_count;i++)
299     if (rd[i].record_type == GNUNET_NAMESTORE_TYPE_PKEY)
300     {
301       GNUNET_assert (sizeof (struct GNUNET_CRYPTO_ShortHashCode) == rd[i].data_size);
302       memcpy (&zone_delegation,
303               rd[i].data,
304               sizeof (struct GNUNET_CRYPTO_ShortHashCode));
305       break;
306     }
307   rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
308   data_size = GNUNET_NAMESTORE_records_get_size (rd_count, rd);
309   if (data_size > 64 * 65536)
310   {
311     GNUNET_break (0);
312     return GNUNET_SYSERR;
313   }
314   {
315     char data[data_size];
316     uint64_t expire_be = GNUNET_htonll (expire.abs_value);
317     uint64_t rvalue_be = GNUNET_htonll (rvalue);
318     uint32_t rd_count_be = htonl ((uint32_t) rd_count);
319     const char *paramValues[] = {
320       (const char *) zone_key,
321       (const char *) name,
322       (const char *) &rd_count_be,
323       (const char *) data,
324       (const char *) &expire_be,
325       (const char *) signature,
326       (const char *) &zone_delegation,
327       (const char *) &zone,
328       (const char *) &nh,
329       (const char *) &rvalue_be
330     };
331     int paramLengths[] = {
332       sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
333       name_len,
334       sizeof (uint32_t),
335       data_size,
336       sizeof (uint64_t),
337       sizeof (struct GNUNET_CRYPTO_RsaSignature), 
338       sizeof (struct GNUNET_CRYPTO_ShortHashCode),
339       sizeof (struct GNUNET_CRYPTO_ShortHashCode),
340       sizeof (struct GNUNET_CRYPTO_ShortHashCode),
341       sizeof (uint64_t)
342     };
343     const int paramFormats[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
344
345     if (data_size != GNUNET_NAMESTORE_records_serialize (rd_count, rd,
346                                                          data_size, data))
347     {
348       GNUNET_break (0);
349       return GNUNET_SYSERR;
350     }
351     ret =
352       PQexecPrepared (plugin->dbh, "put_records", 10, paramValues, paramLengths,
353                       paramFormats, 1);
354     if (GNUNET_OK !=
355         GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "put_records"))
356       return GNUNET_SYSERR;
357     PQclear (ret);
358   }
359   return GNUNET_OK;  
360 }
361
362
363 /**
364  * The given 'postgres' result was obtained from the database.
365  * Parse the record and give it to the iterator.
366  *
367  * @param plugin plugin context
368  * @param stmt_name name of the prepared statement that was executed
369  * @param res result from postgres to interpret (and then clean up)
370  * @param iter iterator to call with the result
371  * @param iter_cls closure for 'iter'
372  * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error
373  *       'iter' will have been called unless the return value is 'GNUNET_SYSERR'
374  */
375 static int
376 get_record_and_call_iterator (struct Plugin *plugin,
377                               const char *stmt_name,
378                               PGresult *res,
379                               GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
380 {
381   unsigned int record_count;
382   size_t data_size;
383   const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key;
384   const struct GNUNET_CRYPTO_RsaSignature *sig;
385   struct GNUNET_TIME_Absolute expiration;
386   const char *data;
387   const char *name;
388   unsigned int cnt;
389   size_t name_len;
390
391   if (GNUNET_OK !=
392       GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK, "PQexecPrepared",
393                                     stmt_name))
394   {
395     LOG (GNUNET_ERROR_TYPE_DEBUG,
396          "Ending iteration (postgres error)\n");
397     return GNUNET_SYSERR;
398   }
399
400   if (0 == (cnt = PQntuples (res)))
401   {
402     /* no result */
403     LOG (GNUNET_ERROR_TYPE_DEBUG, 
404          "Ending iteration (no more results)\n");
405     PQclear (res);
406     iter (iter_cls, NULL, GNUNET_TIME_UNIT_ZERO_ABS, NULL, 0, NULL, NULL);
407     return GNUNET_NO;
408   }
409   GNUNET_assert (1 == cnt);
410   if ((6 != PQnfields (res)) || 
411       (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) != PQgetlength (res, 0, 0)) || 
412       (sizeof (uint32_t) != PQfsize (res, 2)) || 
413       (sizeof (uint64_t) != PQfsize (res, 4)) || 
414       (sizeof (struct GNUNET_CRYPTO_RsaSignature) != PQgetlength (res, 0, 5)))
415   {
416     GNUNET_break (0);
417     PQclear (res);
418     return GNUNET_SYSERR;
419   }
420   zone_key = (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *) PQgetvalue (res, 0, 0);
421   name = PQgetvalue (res, 0, 1);
422   name_len = PQgetlength (res, 0, 1);
423   record_count = ntohl (*(uint32_t *) PQgetvalue (res, 0, 2));
424   data_size = PQgetlength (res, 0, 3);
425   data = PQgetvalue (res, 0, 3);
426   expiration.abs_value =
427     GNUNET_ntohll (*(uint64_t *) PQgetvalue (res, 0, 4));
428   sig = (const struct GNUNET_CRYPTO_RsaSignature*) PQgetvalue (res, 0, 5);
429   if (record_count > 64 * 1024)
430   {
431     /* sanity check, don't stack allocate far too much just
432        because database might contain a large value here */
433     GNUNET_break (0);
434     PQclear (res);
435     return GNUNET_SYSERR;
436   }
437   {
438     struct GNUNET_NAMESTORE_RecordData rd[record_count];
439     char buf[name_len + 1];
440     
441     memcpy (buf, name, name_len);
442     buf[name_len] = '\0';
443     if (GNUNET_OK !=
444         GNUNET_NAMESTORE_records_deserialize (data_size, data,
445                                               record_count, rd))
446     {
447       GNUNET_break (0);
448       PQclear (res);
449       return GNUNET_SYSERR;
450     }
451     iter (iter_cls, zone_key, expiration, buf, 
452           record_count, rd, sig);
453   }
454   PQclear (res);
455   return GNUNET_OK;
456 }
457   
458   
459 /**
460  * Iterate over the results for a particular key and zone in the
461  * datastore.  Will return at most one result to the iterator.
462  *
463  * @param cls closure (internal context for the plugin)
464  * @param zone hash of public key of the zone, NULL to iterate over all zones
465  * @param name name as string, NULL to iterate over all records of the zone
466  * @param offset offset in the list of all matching records
467  * @param iter function to call with the result
468  * @param iter_cls closure for iter
469  * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error
470  *       'iter' will have been called unless the return value is 'GNUNET_SYSERR'
471  */
472 static int 
473 namestore_postgres_iterate_records (void *cls, 
474                                   const struct GNUNET_CRYPTO_ShortHashCode *zone,
475                                   const char *name,
476                                   uint64_t offset,
477                                   GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
478 {
479   struct Plugin *plugin = cls;
480   const char *stmt_name;
481   struct GNUNET_CRYPTO_ShortHashCode name_hase;
482   uint64_t offset_be = GNUNET_htonll (offset);
483   const char *paramValues[] = {
484     (const char *) zone,
485     (const char *) &name_hase,
486     (const char *) &offset_be,
487     (const char *) zone,
488     (const char *) &offset_be,
489   };
490   int paramLengths[] = {
491     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
492     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
493     sizeof (uint64_t),
494     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
495     sizeof (uint64_t)
496   };
497   const int paramFormats[] = { 1, 1, 1, 1, 1 };
498   unsigned int num_params;
499   unsigned int first_param;
500   PGresult *res;
501
502   if (NULL == zone)
503     if (NULL == name)
504     {
505       stmt_name = "iterate_all";
506       num_params = 1;
507       first_param = 2;
508     }
509     else
510     {
511       GNUNET_CRYPTO_short_hash (name, strlen(name), &name_hase);
512       stmt_name = "iterate_by_name";
513       num_params = 2;
514       first_param = 1;
515     }
516   else
517     if (NULL == name)
518     {
519       stmt_name = "iterate_by_zone";
520       num_params = 2;
521       first_param = 3;
522     }
523     else
524     {
525       GNUNET_CRYPTO_short_hash (name, strlen(name), &name_hase);
526       stmt_name = "iterate_records";
527       num_params = 3;
528       first_param = 0;
529     }
530   res =
531       PQexecPrepared (plugin->dbh, stmt_name, num_params, 
532                       &paramValues[first_param],
533                       &paramLengths[first_param],
534                       &paramFormats[first_param], 1);
535   return get_record_and_call_iterator (plugin, stmt_name, res, iter, iter_cls);
536 }
537
538
539 /**
540  * Look for an existing PKEY delegation record for a given public key.
541  * Returns at most one result to the iterator.
542  *
543  * @param cls closure (internal context for the plugin)
544  * @param zone hash of public key of the zone to look up in, never NULL
545  * @param value_zone hash of the public key of the target zone (value), never NULL
546  * @param iter function to call with the result
547  * @param iter_cls closure for iter
548  * @return GNUNET_OK on success, GNUNET_NO if there were no results, GNUNET_SYSERR on error
549  *       'iter' will have been called unless the return value is 'GNUNET_SYSERR'
550  */
551 static int
552 namestore_postgres_zone_to_name (void *cls, 
553                                const struct GNUNET_CRYPTO_ShortHashCode *zone,
554                                const struct GNUNET_CRYPTO_ShortHashCode *value_zone,
555                                GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
556 {
557   struct Plugin *plugin = cls;
558   const char *paramValues[] = {
559     (const char *) zone,
560     (const char *) value_zone
561   };
562   int paramLengths[] = {
563     sizeof (struct GNUNET_CRYPTO_ShortHashCode),
564     sizeof (struct GNUNET_CRYPTO_ShortHashCode)
565   };
566   const int paramFormats[] = { 1, 1 };
567   PGresult *res;
568
569   res =
570     PQexecPrepared (plugin->dbh, "zone_to_name", 2,
571                     paramValues, paramLengths, paramFormats, 1);
572   return get_record_and_call_iterator (plugin, "zone_to_name", res, iter, iter_cls);
573 }
574
575
576 /**
577  * Delete an entire zone (all records).  Not used in normal operation.
578  *
579  * @param cls closure (internal context for the plugin)
580  * @param zone zone to delete
581  */
582 static void 
583 namestore_postgres_delete_zone (void *cls,
584                               const struct GNUNET_CRYPTO_ShortHashCode *zone)
585 {
586   struct Plugin *plugin = cls;
587   PGresult *ret;
588   const char *paramValues[] = {
589     (const char *) zone,
590   };
591   int paramLengths[] = {
592     sizeof (struct GNUNET_CRYPTO_ShortHashCode)
593   };
594   const int paramFormats[] = { 1 };
595
596   ret =
597       PQexecPrepared (plugin->dbh, "delete_zone", 1, paramValues, paramLengths,
598                       paramFormats, 1);
599   if (GNUNET_OK !=
600       GNUNET_POSTGRES_check_result (plugin->dbh, ret, PGRES_COMMAND_OK, "PQexecPrepared", "delete_zone"))
601   {
602     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
603                 "Deleting zone failed!\n");             
604     return;
605   }
606   PQclear (ret);
607 }
608
609
610 /**
611  * Shutdown database connection and associate data
612  * structures.
613  *
614  * @param plugin the plugin context (state for this module)
615  */
616 static void
617 database_shutdown (struct Plugin *plugin)
618 {
619   PQfinish (plugin->dbh);
620   plugin->dbh = NULL;
621 }
622
623
624 /**
625  * Entry point for the plugin.
626  *
627  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
628  * @return NULL on error, othrewise the plugin context
629  */
630 void *
631 libgnunet_plugin_namestore_postgres_init (void *cls)
632 {
633   static struct Plugin plugin;
634   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
635   struct GNUNET_NAMESTORE_PluginFunctions *api;
636
637   if (NULL != plugin.cfg)
638     return NULL;                /* can only initialize once! */
639   memset (&plugin, 0, sizeof (struct Plugin));
640   plugin.cfg = cfg;  
641   if (GNUNET_OK != database_setup (&plugin))
642   {
643     database_shutdown (&plugin);
644     return NULL;
645   }
646   api = GNUNET_malloc (sizeof (struct GNUNET_NAMESTORE_PluginFunctions));
647   api->cls = &plugin;
648   api->put_records = &namestore_postgres_put_records;
649   api->remove_records = &namestore_postgres_remove_records;
650   api->iterate_records = &namestore_postgres_iterate_records;
651   api->zone_to_name = &namestore_postgres_zone_to_name;
652   api->delete_zone = &namestore_postgres_delete_zone;
653   LOG (GNUNET_ERROR_TYPE_INFO, 
654        _("Postgres database running\n"));
655   return api;
656 }
657
658
659 /**
660  * Exit point from the plugin.
661  *
662  * @param cls the plugin context (as returned by "init")
663  * @return always NULL
664  */
665 void *
666 libgnunet_plugin_namestore_postgres_done (void *cls)
667 {
668   struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
669   struct Plugin *plugin = api->cls;
670
671   database_shutdown (plugin);
672   plugin->cfg = NULL;
673   GNUNET_free (api);
674   LOG (GNUNET_ERROR_TYPE_DEBUG, 
675        "postgres plugin is finished\n");
676   return NULL;
677 }
678
679 /* end of plugin_namestore_postgres.c */