reduce loop counters to more practical levels
[oweals/gnunet.git] / src / namestore / plugin_namestore_sqlite.c
1  /*
2   * This file is part of GNUnet
3   * Copyright (C) 2009-2017 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18   * Boston, MA 02110-1301, USA.
19   */
20
21 /**
22  * @file namestore/plugin_namestore_sqlite.c
23  * @brief sqlite-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_gnsrecord_lib.h"
31 #include "gnunet_sq_lib.h"
32 #include "namestore.h"
33 #include <sqlite3.h>
34
35 /**
36  * After how many ms "busy" should a DB operation fail for good?  A
37  * low value makes sure that we are more responsive to requests
38  * (especially PUTs).  A high value guarantees a higher success rate
39  * (SELECTs in iterate can take several seconds despite LIMIT=1).
40  *
41  * The default value of 1s should ensure that users do not experience
42  * huge latencies while at the same time allowing operations to
43  * succeed with reasonable probability.
44  */
45 #define BUSY_TIMEOUT_MS 1000
46
47
48 /**
49  * Log an error message at log-level 'level' that indicates
50  * a failure of the command 'cmd' on file 'filename'
51  * with the message given by strerror(errno).
52  */
53 #define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, "namestore-sqlite", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while(0)
54
55 #define LOG(kind,...) GNUNET_log_from (kind, "namestore-sqlite", __VA_ARGS__)
56
57
58 /**
59  * Context for all functions in this plugin.
60  */
61 struct Plugin
62 {
63
64   const struct GNUNET_CONFIGURATION_Handle *cfg;
65
66   /**
67    * Database filename.
68    */
69   char *fn;
70
71   /**
72    * Native SQLite database handle.
73    */
74   sqlite3 *dbh;
75
76   /**
77    * Precompiled SQL to store records.
78    */
79   sqlite3_stmt *store_records;
80
81   /**
82    * Precompiled SQL to deltete existing records.
83    */
84   sqlite3_stmt *delete_records;
85
86   /**
87    * Precompiled SQL for iterate records within a zone.
88    */
89   sqlite3_stmt *iterate_zone;
90
91   /**
92    * Precompiled SQL for iterate all records within all zones.
93    */
94   sqlite3_stmt *iterate_all_zones;
95
96   /**
97    * Precompiled SQL to for reverse lookup based on PKEY.
98    */
99   sqlite3_stmt *zone_to_name;
100
101   /**
102    * Precompiled SQL to lookup records based on label.
103    */
104   sqlite3_stmt *lookup_label;
105 };
106
107
108 /**
109  * Initialize the database connections and associated
110  * data structures (create tables and indices
111  * as needed as well).
112  *
113  * @param plugin the plugin context (state for this module)
114  * @return #GNUNET_OK on success
115  */
116 static int
117 database_setup (struct Plugin *plugin)
118 {
119   char *sqlite_filename;
120   struct GNUNET_SQ_ExecuteStatement es[] = {
121     GNUNET_SQ_make_try_execute ("PRAGMA temp_store=MEMORY"),
122     GNUNET_SQ_make_try_execute ("PRAGMA synchronous=NORMAL"),
123     GNUNET_SQ_make_try_execute ("PRAGMA legacy_file_format=OFF"),
124     GNUNET_SQ_make_try_execute ("PRAGMA auto_vacuum=INCREMENTAL"),
125     GNUNET_SQ_make_try_execute ("PRAGMA encoding=\"UTF-8\""),
126     GNUNET_SQ_make_try_execute ("PRAGMA locking_mode=EXCLUSIVE"),
127     GNUNET_SQ_make_try_execute ("PRAGMA page_size=4092"),
128     GNUNET_SQ_make_execute ("CREATE TABLE IF NOT EXISTS ns098records ("
129                             " uid INTEGER PRIMARY KEY,"
130                             " zone_private_key BLOB NOT NULL,"
131                             " pkey BLOB,"
132                             " rvalue INT8 NOT NULL,"
133                             " record_count INT NOT NULL,"
134                             " record_data BLOB NOT NULL,"
135                             " label TEXT NOT NULL"
136                             ")"),
137     GNUNET_SQ_make_try_execute ("CREATE INDEX IF NOT EXISTS ir_pkey_reverse "
138                                 "ON ns098records (zone_private_key,pkey)"),
139     GNUNET_SQ_make_try_execute ("CREATE INDEX IF NOT EXISTS ir_pkey_iter "
140                                 "ON ns098records (zone_private_key,uid)"),
141     GNUNET_SQ_EXECUTE_STATEMENT_END
142   };
143   struct GNUNET_SQ_PrepareStatement ps[] = {
144     GNUNET_SQ_make_prepare ("INSERT INTO ns098records "
145                             "(zone_private_key,pkey,rvalue,record_count,record_data,label)"
146                             " VALUES (?, ?, ?, ?, ?, ?)",
147                             &plugin->store_records),
148     GNUNET_SQ_make_prepare ("DELETE FROM ns098records "
149                             "WHERE zone_private_key=? AND label=?",
150                             &plugin->delete_records),
151     GNUNET_SQ_make_prepare ("SELECT uid,record_count,record_data,label"
152                             " FROM ns098records"
153                             " WHERE zone_private_key=? AND pkey=?",
154                             &plugin->zone_to_name),
155     GNUNET_SQ_make_prepare ("SELECT uid,record_count,record_data,label"
156                             " FROM ns098records"
157                             " WHERE zone_private_key=? AND _rowid_ >= ?"
158                             " ORDER BY _rowid_ ASC"
159                             " LIMIT ?",
160                             &plugin->iterate_zone),
161     GNUNET_SQ_make_prepare ("SELECT uid,record_count,record_data,label,zone_private_key"
162                             " FROM ns098records"
163                             " WHERE _rowid_ >= ?"
164                             " ORDER BY _rowid_ ASC"
165                             " LIMIT ?",
166                             &plugin->iterate_all_zones),
167     GNUNET_SQ_make_prepare ("SELECT uid,record_count,record_data,label,zone_private_key"
168                             " FROM ns098records"
169                             " WHERE zone_private_key=? AND label=?",
170                             &plugin->lookup_label),
171     GNUNET_SQ_PREPARE_END
172   };
173
174   if (GNUNET_OK !=
175       GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
176                                                "namestore-sqlite",
177                                                "FILENAME",
178                                                &sqlite_filename))
179   {
180     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
181                                "namestore-sqlite",
182                                "FILENAME");
183     return GNUNET_SYSERR;
184   }
185   if (GNUNET_OK !=
186       GNUNET_DISK_file_test (sqlite_filename))
187   {
188     if (GNUNET_OK !=
189         GNUNET_DISK_directory_create_for_file (sqlite_filename))
190     {
191       GNUNET_break (0);
192       GNUNET_free (sqlite_filename);
193       return GNUNET_SYSERR;
194     }
195   }
196   /* sqlite_filename should be UTF-8-encoded. If it isn't, it's a bug */
197   plugin->fn = sqlite_filename;
198
199   /* Open database and precompile statements */
200   if (SQLITE_OK !=
201       sqlite3_open (plugin->fn,
202                     &plugin->dbh))
203   {
204     LOG (GNUNET_ERROR_TYPE_ERROR,
205          _("Unable to initialize SQLite: %s.\n"),
206          sqlite3_errmsg (plugin->dbh));
207     return GNUNET_SYSERR;
208   }
209   GNUNET_break (SQLITE_OK ==
210                 sqlite3_busy_timeout (plugin->dbh,
211                                       BUSY_TIMEOUT_MS));
212   if (GNUNET_OK !=
213       GNUNET_SQ_exec_statements (plugin->dbh,
214                                  es))
215   {
216     GNUNET_break (0);
217     LOG (GNUNET_ERROR_TYPE_ERROR,
218          _("Failed to setup database at `%s'\n"),
219          plugin->fn);
220     return GNUNET_SYSERR;
221   }
222
223   if (GNUNET_OK !=
224       GNUNET_SQ_prepare (plugin->dbh,
225                          ps))
226   {
227     GNUNET_break (0);
228     LOG (GNUNET_ERROR_TYPE_ERROR,
229          _("Failed to setup database at `%s'\n"),
230          plugin->fn);
231     return GNUNET_SYSERR;
232   }
233   return GNUNET_OK;
234 }
235
236
237 /**
238  * Shutdown database connection and associate data
239  * structures.
240  * @param plugin the plugin context (state for this module)
241  */
242 static void
243 database_shutdown (struct Plugin *plugin)
244 {
245   int result;
246   sqlite3_stmt *stmt;
247
248   if (NULL != plugin->store_records)
249     sqlite3_finalize (plugin->store_records);
250   if (NULL != plugin->delete_records)
251     sqlite3_finalize (plugin->delete_records);
252   if (NULL != plugin->iterate_zone)
253     sqlite3_finalize (plugin->iterate_zone);
254   if (NULL != plugin->iterate_all_zones)
255     sqlite3_finalize (plugin->iterate_all_zones);
256   if (NULL != plugin->zone_to_name)
257     sqlite3_finalize (plugin->zone_to_name);
258   if (NULL != plugin->lookup_label)
259     sqlite3_finalize (plugin->lookup_label);
260   result = sqlite3_close (plugin->dbh);
261   if (result == SQLITE_BUSY)
262   {
263     LOG (GNUNET_ERROR_TYPE_WARNING,
264          _("Tried to close sqlite without finalizing all prepared statements.\n"));
265     stmt = sqlite3_next_stmt (plugin->dbh,
266                               NULL);
267     while (NULL != stmt)
268     {
269       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
270                        "sqlite",
271                        "Closing statement %p\n",
272                        stmt);
273       result = sqlite3_finalize (stmt);
274       if (result != SQLITE_OK)
275         GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
276                          "sqlite",
277                          "Failed to close statement %p: %d\n",
278                          stmt,
279                          result);
280       stmt = sqlite3_next_stmt (plugin->dbh,
281                                 NULL);
282     }
283     result = sqlite3_close (plugin->dbh);
284   }
285   if (SQLITE_OK != result)
286     LOG_SQLITE (plugin,
287                 GNUNET_ERROR_TYPE_ERROR,
288                 "sqlite3_close");
289
290   GNUNET_free_non_null (plugin->fn);
291 }
292
293
294 /**
295  * Store a record in the datastore.  Removes any existing record in the
296  * same zone with the same name.
297  *
298  * @param cls closure (internal context for the plugin)
299  * @param zone_key private key of the zone
300  * @param label name that is being mapped (at most 255 characters long)
301  * @param rd_count number of entries in @a rd array
302  * @param rd array of records with data to store
303  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
304  */
305 static int
306 namestore_sqlite_store_records (void *cls,
307                                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
308                                 const char *label,
309                                 unsigned int rd_count,
310                                 const struct GNUNET_GNSRECORD_Data *rd)
311 {
312   struct Plugin *plugin = cls;
313   int n;
314   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
315   uint64_t rvalue;
316   ssize_t data_size;
317
318   memset (&pkey,
319           0,
320           sizeof (pkey));
321   for (unsigned int i=0;i<rd_count;i++)
322     if (GNUNET_GNSRECORD_TYPE_PKEY == rd[i].record_type)
323     {
324       GNUNET_break (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) ==
325                     rd[i].data_size);
326       GNUNET_memcpy (&pkey,
327                      rd[i].data,
328                      rd[i].data_size);
329       break;
330     }
331   rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
332                                      UINT64_MAX);
333   data_size = GNUNET_GNSRECORD_records_get_size (rd_count,
334                                                  rd);
335   if (data_size < 0)
336   {
337     GNUNET_break (0);
338     return GNUNET_SYSERR;
339   }
340   if (data_size > 64 * 65536)
341   {
342     GNUNET_break (0);
343     return GNUNET_SYSERR;
344   }
345   {
346     /* First delete 'old' records */
347     char data[data_size];
348     struct GNUNET_SQ_QueryParam dparams[] = {
349       GNUNET_SQ_query_param_auto_from_type (zone_key),
350       GNUNET_SQ_query_param_string (label),
351       GNUNET_SQ_query_param_end
352     };
353     ssize_t ret;
354
355     ret = GNUNET_GNSRECORD_records_serialize (rd_count,
356                                               rd,
357                                               data_size,
358                                               data);
359     if ( (ret < 0) ||
360          (data_size != ret) )
361     {
362       GNUNET_break (0);
363       return GNUNET_SYSERR;
364     }
365     if (GNUNET_OK !=
366         GNUNET_SQ_bind (plugin->delete_records,
367                         dparams))
368     {
369       LOG_SQLITE (plugin,
370                   GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
371                   "sqlite3_bind_XXXX");
372       GNUNET_SQ_reset (plugin->dbh,
373                        plugin->delete_records);
374       return GNUNET_SYSERR;
375
376     }
377     n = sqlite3_step (plugin->delete_records);
378     GNUNET_SQ_reset (plugin->dbh,
379                      plugin->delete_records);
380
381     if (0 != rd_count)
382     {
383       uint32_t rd_count32 = (uint32_t) rd_count;
384       struct GNUNET_SQ_QueryParam sparams[] = {
385         GNUNET_SQ_query_param_auto_from_type (zone_key),
386         GNUNET_SQ_query_param_auto_from_type (&pkey),
387         GNUNET_SQ_query_param_uint64 (&rvalue),
388         GNUNET_SQ_query_param_uint32 (&rd_count32),
389         GNUNET_SQ_query_param_fixed_size (data, data_size),
390         GNUNET_SQ_query_param_string (label),
391         GNUNET_SQ_query_param_end
392       };
393
394       if (GNUNET_OK !=
395           GNUNET_SQ_bind (plugin->store_records,
396                           sparams))
397       {
398         LOG_SQLITE (plugin,
399                     GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
400                     "sqlite3_bind_XXXX");
401         GNUNET_SQ_reset (plugin->dbh,
402                          plugin->store_records);
403         return GNUNET_SYSERR;
404       }
405       n = sqlite3_step (plugin->store_records);
406       GNUNET_SQ_reset (plugin->dbh,
407                        plugin->store_records);
408     }
409   }
410   switch (n)
411   {
412   case SQLITE_DONE:
413     if (0 != rd_count)
414       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
415                        "sqlite",
416                        "Record stored\n");
417     else
418       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
419                        "sqlite",
420                        "Record deleted\n");
421     return GNUNET_OK;
422   case SQLITE_BUSY:
423     LOG_SQLITE (plugin,
424                 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
425                 "sqlite3_step");
426     return GNUNET_NO;
427   default:
428     LOG_SQLITE (plugin,
429                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
430                 "sqlite3_step");
431     return GNUNET_SYSERR;
432   }
433 }
434
435
436 /**
437  * The given 'sqlite' statement has been prepared to be run.
438  * It will return a record which should be given to the iterator.
439  * Runs the statement and parses the returned record.
440  *
441  * @param plugin plugin context
442  * @param stmt to run (and then clean up)
443  * @param zone_key private key of the zone
444  * @param limit maximum number of results to fetch
445  * @param iter iterator to call with the result
446  * @param iter_cls closure for @a iter
447  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
448  */
449 static int
450 get_records_and_call_iterator (struct Plugin *plugin,
451                                sqlite3_stmt *stmt,
452                                const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
453                                uint64_t limit,
454                                GNUNET_NAMESTORE_RecordIterator iter,
455                                void *iter_cls)
456 {
457   int ret;
458   int sret;
459
460   ret = GNUNET_OK;
461   for (uint64_t i = 0;i<limit;i++)
462   {
463     sret = sqlite3_step (stmt);
464
465     if (SQLITE_DONE == sret)
466     {
467       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
468                   "Iteration done (no results)\n");
469       ret = GNUNET_NO;
470       break;
471     }
472     if (SQLITE_ROW != sret)
473     {
474       LOG_SQLITE (plugin,
475                   GNUNET_ERROR_TYPE_ERROR,
476                   "sqlite_step");
477       ret = GNUNET_SYSERR;
478       break;
479     }
480
481     {
482       uint64_t seq;
483       uint32_t record_count;
484       size_t data_size;
485       void *data;
486       char *label;
487       struct GNUNET_CRYPTO_EcdsaPrivateKey zk;
488       struct GNUNET_SQ_ResultSpec rs[] = {
489         GNUNET_SQ_result_spec_uint64 (&seq),
490         GNUNET_SQ_result_spec_uint32 (&record_count),
491         GNUNET_SQ_result_spec_variable_size (&data,
492                                              &data_size),
493         GNUNET_SQ_result_spec_string (&label),
494         GNUNET_SQ_result_spec_end
495       };
496       struct GNUNET_SQ_ResultSpec rsx[] = {
497         GNUNET_SQ_result_spec_uint64 (&seq),
498         GNUNET_SQ_result_spec_uint32 (&record_count),
499         GNUNET_SQ_result_spec_variable_size (&data,
500                                              &data_size),
501         GNUNET_SQ_result_spec_string (&label),
502         GNUNET_SQ_result_spec_auto_from_type (&zk),
503         GNUNET_SQ_result_spec_end
504       };
505
506       if (NULL == zone_key)
507       {
508         zone_key = &zk;
509         ret = GNUNET_SQ_extract_result (stmt,
510                                         rsx);
511       }
512       else
513       {
514         ret = GNUNET_SQ_extract_result (stmt,
515                                         rs);
516       }
517       if ( (GNUNET_OK != ret) ||
518            (record_count > 64 * 1024) )
519       {
520         /* sanity check, don't stack allocate far too much just
521            because database might contain a large value here */
522         GNUNET_break (0);
523         ret = GNUNET_SYSERR;
524         break;
525       }
526       else
527       {
528         struct GNUNET_GNSRECORD_Data rd[record_count];
529
530         if (GNUNET_OK !=
531             GNUNET_GNSRECORD_records_deserialize (data_size,
532                                                   data,
533                                                   record_count,
534                                                   rd))
535         {
536           GNUNET_break (0);
537           ret = GNUNET_SYSERR;
538           break;
539         }
540         else
541         {
542           if (NULL != iter)
543             iter (iter_cls,
544                   seq + 1,
545                   zone_key,
546                   label,
547                   record_count,
548                   rd);
549         }
550       }
551       GNUNET_SQ_cleanup_result (rs);
552     }
553   }
554   GNUNET_SQ_reset (plugin->dbh,
555                    stmt);
556   return ret;
557 }
558
559
560 /**
561  * Lookup records in the datastore for which we are the authority.
562  *
563  * @param cls closure (internal context for the plugin)
564  * @param zone private key of the zone
565  * @param label name of the record in the zone
566  * @param iter function to call with the result
567  * @param iter_cls closure for @a iter
568  * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR
569  */
570 static int
571 namestore_sqlite_lookup_records (void *cls,
572                                  const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
573                                  const char *label,
574                                  GNUNET_NAMESTORE_RecordIterator iter,
575                                  void *iter_cls)
576 {
577   struct Plugin *plugin = cls;
578   struct GNUNET_SQ_QueryParam params[] = {
579     GNUNET_SQ_query_param_auto_from_type (zone),
580     GNUNET_SQ_query_param_string (label),
581     GNUNET_SQ_query_param_end
582   };
583
584   if (NULL == zone)
585   {
586     GNUNET_break (0);
587     return GNUNET_SYSERR;
588   }
589   if (GNUNET_OK !=
590       GNUNET_SQ_bind (plugin->lookup_label,
591                       params))
592   {
593     LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
594                 "sqlite3_bind_XXXX");
595     GNUNET_SQ_reset (plugin->dbh,
596                      plugin->lookup_label);
597     return GNUNET_SYSERR;
598   }
599   return get_records_and_call_iterator (plugin,
600                                         plugin->lookup_label,
601                                         zone,
602                                         1,
603                                         iter,
604                                         iter_cls);
605 }
606
607
608 /**
609  * Iterate over the results for a particular key and zone in the
610  * datastore.  Will return at most one result to the iterator.
611  *
612  * @param cls closure (internal context for the plugin)
613  * @param zone hash of public key of the zone, NULL to iterate over all zones
614  * @param serial serial number to exclude in the list of all matching records
615  * @param limit maximum number of results to return
616  * @param iter function to call with the result
617  * @param iter_cls closure for @a iter
618  * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error
619  */
620 static int
621 namestore_sqlite_iterate_records (void *cls,
622                                   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
623                                   uint64_t serial,
624                                   uint64_t limit,
625                                   GNUNET_NAMESTORE_RecordIterator iter,
626                                   void *iter_cls)
627 {
628   struct Plugin *plugin = cls;
629   sqlite3_stmt *stmt;
630   int err;
631
632   if (NULL == zone)
633   {
634     struct GNUNET_SQ_QueryParam params[] = {
635       GNUNET_SQ_query_param_uint64 (&serial),
636       GNUNET_SQ_query_param_uint64 (&limit),
637       GNUNET_SQ_query_param_end
638     };
639
640     stmt = plugin->iterate_all_zones;
641     err = GNUNET_SQ_bind (stmt,
642                           params);
643   }
644   else
645   {
646     struct GNUNET_SQ_QueryParam params[] = {
647       GNUNET_SQ_query_param_auto_from_type (zone),
648       GNUNET_SQ_query_param_uint64 (&serial),
649       GNUNET_SQ_query_param_uint64 (&limit),
650       GNUNET_SQ_query_param_end
651     };
652
653     stmt = plugin->iterate_zone;
654     err = GNUNET_SQ_bind (stmt,
655                           params);
656   }
657   if (GNUNET_OK != err)
658   {
659     LOG_SQLITE (plugin,
660                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
661                 "sqlite3_bind_XXXX");
662     GNUNET_SQ_reset (plugin->dbh,
663                      stmt);
664     return GNUNET_SYSERR;
665   }
666   return get_records_and_call_iterator (plugin,
667                                         stmt,
668                                         zone,
669                                         limit,
670                                         iter,
671                                         iter_cls);
672 }
673
674
675 /**
676  * Look for an existing PKEY delegation record for a given public key.
677  * Returns at most one result to the iterator.
678  *
679  * @param cls closure (internal context for the plugin)
680  * @param zone private key of the zone to look up in, never NULL
681  * @param value_zone public key of the target zone (value), never NULL
682  * @param iter function to call with the result
683  * @param iter_cls closure for @a iter
684  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
685  */
686 static int
687 namestore_sqlite_zone_to_name (void *cls,
688                                const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
689                                const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone,
690                                GNUNET_NAMESTORE_RecordIterator iter,
691                                void *iter_cls)
692 {
693   struct Plugin *plugin = cls;
694   struct GNUNET_SQ_QueryParam params[] = {
695     GNUNET_SQ_query_param_auto_from_type (zone),
696     GNUNET_SQ_query_param_auto_from_type (value_zone),
697     GNUNET_SQ_query_param_end
698   };
699
700   if (GNUNET_OK !=
701       GNUNET_SQ_bind (plugin->zone_to_name,
702                       params))
703   {
704     LOG_SQLITE (plugin,
705                 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
706                 "sqlite3_bind_XXXX");
707     GNUNET_SQ_reset (plugin->dbh,
708                      plugin->zone_to_name);
709     return GNUNET_SYSERR;
710   }
711   LOG (GNUNET_ERROR_TYPE_DEBUG,
712        "Performing reverse lookup for `%s'\n",
713        GNUNET_GNSRECORD_z2s (value_zone));
714   return get_records_and_call_iterator (plugin,
715                                         plugin->zone_to_name,
716                                         zone,
717                                         1,
718                                         iter,
719                                         iter_cls);
720 }
721
722
723 /**
724  * Entry point for the plugin.
725  *
726  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
727  * @return NULL on error, otherwise the plugin context
728  */
729 void *
730 libgnunet_plugin_namestore_sqlite_init (void *cls)
731 {
732   static struct Plugin plugin;
733   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
734   struct GNUNET_NAMESTORE_PluginFunctions *api;
735
736   if (NULL != plugin.cfg)
737     return NULL;                /* can only initialize once! */
738   memset (&plugin,
739           0,
740           sizeof (struct Plugin));
741   plugin.cfg = cfg;
742   if (GNUNET_OK != database_setup (&plugin))
743   {
744     database_shutdown (&plugin);
745     return NULL;
746   }
747   api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions);
748   api->cls = &plugin;
749   api->store_records = &namestore_sqlite_store_records;
750   api->iterate_records = &namestore_sqlite_iterate_records;
751   api->zone_to_name = &namestore_sqlite_zone_to_name;
752   api->lookup_records = &namestore_sqlite_lookup_records;
753   LOG (GNUNET_ERROR_TYPE_INFO,
754        _("Sqlite database running\n"));
755   return api;
756 }
757
758
759 /**
760  * Exit point from the plugin.
761  *
762  * @param cls the plugin context (as returned by "init")
763  * @return always NULL
764  */
765 void *
766 libgnunet_plugin_namestore_sqlite_done (void *cls)
767 {
768   struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
769   struct Plugin *plugin = api->cls;
770
771   database_shutdown (plugin);
772   plugin->cfg = NULL;
773   GNUNET_free (api);
774   LOG (GNUNET_ERROR_TYPE_DEBUG,
775        "sqlite plugin is finished\n");
776   return NULL;
777 }
778
779 /* end of plugin_namestore_sqlite.c */