GNUnet  0.11.x
plugin_namestore_postgres.c
Go to the documentation of this file.
1 /*
2  * This file is part of GNUnet
3  * Copyright (C) 2009-2013, 2016-2018 GNUnet e.V.
4  *
5  * GNUnet is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Affero General Public License as published
7  * by the Free Software Foundation, either version 3 of the License,
8  * or (at your 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  * Affero General Public License for more details.
14  *
15  * You should have received a copy of the GNU Affero General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 
26 #include "platform.h"
29 #include "gnunet_gnsrecord_lib.h"
30 #include "gnunet_pq_lib.h"
31 #include "namestore.h"
32 
33 
34 #define LOG(kind, ...) GNUNET_log_from (kind, "namestore-postgres", __VA_ARGS__)
35 
36 
40 struct Plugin
41 {
45  const struct GNUNET_CONFIGURATION_Handle *cfg;
46 
50  struct GNUNET_PQ_Context *dbh;
51 };
52 
53 
62 static int
64 {
65  struct GNUNET_PQ_ExecuteStatement es_temporary =
67  "CREATE TEMPORARY TABLE IF NOT EXISTS ns098records ("
68  " seq BIGSERIAL PRIMARY KEY,"
69  " zone_private_key BYTEA NOT NULL DEFAULT '',"
70  " pkey BYTEA DEFAULT '',"
71  " rvalue BYTEA NOT NULL DEFAULT '',"
72  " record_count INTEGER NOT NULL DEFAULT 0,"
73  " record_data BYTEA NOT NULL DEFAULT '',"
74  " label TEXT NOT NULL DEFAULT '',"
75  " CONSTRAINT zl UNIQUE (zone_private_key,label)"
76  ")");
77  struct GNUNET_PQ_ExecuteStatement es_default =
78  GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS ns098records ("
79  " seq BIGSERIAL PRIMARY KEY,"
80  " zone_private_key BYTEA NOT NULL DEFAULT '',"
81  " pkey BYTEA DEFAULT '',"
82  " rvalue BYTEA NOT NULL DEFAULT '',"
83  " record_count INTEGER NOT NULL DEFAULT 0,"
84  " record_data BYTEA NOT NULL DEFAULT '',"
85  " label TEXT NOT NULL DEFAULT '',"
86  " CONSTRAINT zl UNIQUE (zone_private_key,label)"
87  ")");
88  const struct GNUNET_PQ_ExecuteStatement *cr;
90 
91  if (GNUNET_YES ==
93  "namestore-postgres",
94  "TEMPORARY_TABLE"))
95  {
96  cr = &es_temporary;
97  }
98  else
99  {
100  cr = &es_default;
101  }
102 
103  if (GNUNET_YES ==
105  "namestore-postgres",
106  "ASYNC_COMMIT"))
107  sc = GNUNET_PQ_make_try_execute ("SET synchronous_commit TO off");
108 
109  {
110  struct GNUNET_PQ_ExecuteStatement es[] = {
111  *cr,
112  GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS ir_pkey_reverse "
113  "ON ns098records (zone_private_key,pkey)"),
114  GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS ir_pkey_iter "
115  "ON ns098records (zone_private_key,seq)"),
116  GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS ir_label "
117  "ON ns098records (label)"),
118  GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS zone_label "
119  "ON ns098records (zone_private_key,label)"),
120  sc,
122  };
123  struct GNUNET_PQ_PreparedStatement ps[] = {
124  GNUNET_PQ_make_prepare ("store_records",
125  "INSERT INTO ns098records"
126  " (zone_private_key, pkey, rvalue, record_count, record_data, label)"
127  " VALUES ($1, $2, $3, $4, $5, $6)"
128  " ON CONFLICT ON CONSTRAINT zl"
129  " DO UPDATE"
130  " SET pkey=$2,rvalue=$3,record_count=$4,record_data=$5"
131  " WHERE ns098records.zone_private_key = $1"
132  " AND ns098records.label = $6",
133  6),
134  GNUNET_PQ_make_prepare ("delete_records",
135  "DELETE FROM ns098records "
136  "WHERE zone_private_key=$1 AND label=$2",
137  2),
138  GNUNET_PQ_make_prepare ("zone_to_name",
139  "SELECT seq,record_count,record_data,label FROM ns098records"
140  " WHERE zone_private_key=$1 AND pkey=$2",
141  2),
142  GNUNET_PQ_make_prepare ("iterate_zone",
143  "SELECT seq,record_count,record_data,label FROM ns098records "
144  "WHERE zone_private_key=$1 AND seq > $2 ORDER BY seq ASC LIMIT $3",
145  3),
146  GNUNET_PQ_make_prepare ("iterate_all_zones",
147  "SELECT seq,record_count,record_data,label,zone_private_key"
148  " FROM ns098records WHERE seq > $1 ORDER BY seq ASC LIMIT $2",
149  2),
150  GNUNET_PQ_make_prepare ("lookup_label",
151  "SELECT seq,record_count,record_data,label "
152  "FROM ns098records WHERE zone_private_key=$1 AND label=$2",
153  2),
155  };
156 
157  plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
158  "namestore-postgres",
159  NULL,
160  es,
161  ps);
162  }
163  if (NULL == plugin->dbh)
164  return GNUNET_SYSERR;
165  return GNUNET_OK;
166 }
167 
168 
180 static int
182  const struct
183  GNUNET_IDENTITY_PrivateKey *zone_key,
184  const char *label,
185  unsigned int rd_count,
186  const struct GNUNET_GNSRECORD_Data *rd)
187 {
188  struct Plugin *plugin = cls;
189  struct GNUNET_IDENTITY_PublicKey pkey;
190  uint64_t rvalue;
191  uint32_t rd_count32 = (uint32_t) rd_count;
192  ssize_t data_size;
193 
194  memset (&pkey,
195  0,
196  sizeof(pkey));
197  for (unsigned int i = 0; i < rd_count; i++)
198  if (GNUNET_YES ==
199  GNUNET_GNSRECORD_is_zonekey_type (rd[i].record_type))
200  {
203  rd[i].data_size,
204  rd[i].record_type,
205  &pkey));
206  break;
207  }
209  UINT64_MAX);
210  data_size = GNUNET_GNSRECORD_records_get_size (rd_count,
211  rd);
212  if (data_size < 0)
213  {
214  GNUNET_break (0);
215  return GNUNET_SYSERR;
216  }
217  if (data_size >= UINT16_MAX)
218  {
219  GNUNET_break (0);
220  return GNUNET_SYSERR;
221  }
222  /* if record set is empty, delete existing records */
223  if (0 == rd_count)
224  {
225  struct GNUNET_PQ_QueryParam params[] = {
229  };
231 
233  "delete_records",
234  params);
237  {
238  GNUNET_break (0);
239  return GNUNET_SYSERR;
240  }
242  "postgres",
243  "Record deleted\n");
244  return GNUNET_OK;
245  }
246  /* otherwise, UPSERT (i.e. UPDATE if exists, otherwise INSERT) */
247  {
248  char data[data_size];
249  struct GNUNET_PQ_QueryParam params[] = {
253  GNUNET_PQ_query_param_uint32 (&rd_count32),
254  GNUNET_PQ_query_param_fixed_size (data, data_size),
257  };
259  ssize_t ret;
260 
261  ret = GNUNET_GNSRECORD_records_serialize (rd_count,
262  rd,
263  data_size,
264  data);
265  if ((ret < 0) ||
266  (data_size != ret))
267  {
268  GNUNET_break (0);
269  return GNUNET_SYSERR;
270  }
271 
273  "store_records",
274  params);
276  return GNUNET_SYSERR;
277  }
278  return GNUNET_OK;
279 }
280 
281 
286 {
291 
295  void *iter_cls;
296 
301 
306  uint64_t limit;
307 };
308 
309 
318 static void
320  PGresult *res,
321  unsigned int num_results)
322 {
323  struct ParserContext *pc = cls;
324 
325  if (NULL == pc->iter)
326  return; /* no need to do more work */
327  for (unsigned int i = 0; i < num_results; i++)
328  {
329  uint64_t serial;
330  void *data;
331  size_t data_size;
332  uint32_t record_count;
333  char *label;
334  struct GNUNET_IDENTITY_PrivateKey zk;
335  struct GNUNET_PQ_ResultSpec rs_with_zone[] = {
336  GNUNET_PQ_result_spec_uint64 ("seq", &serial),
337  GNUNET_PQ_result_spec_uint32 ("record_count", &record_count),
338  GNUNET_PQ_result_spec_variable_size ("record_data", &data, &data_size),
339  GNUNET_PQ_result_spec_string ("label", &label),
340  GNUNET_PQ_result_spec_auto_from_type ("zone_private_key", &zk),
342  };
343  struct GNUNET_PQ_ResultSpec rs_without_zone[] = {
344  GNUNET_PQ_result_spec_uint64 ("seq", &serial),
345  GNUNET_PQ_result_spec_uint32 ("record_count", &record_count),
346  GNUNET_PQ_result_spec_variable_size ("record_data", &data, &data_size),
347  GNUNET_PQ_result_spec_string ("label", &label),
349  };
350  struct GNUNET_PQ_ResultSpec *rs;
351 
352  rs = (NULL == pc->zone_key) ? rs_with_zone : rs_without_zone;
353  if (GNUNET_YES !=
355  rs,
356  i))
357  {
358  GNUNET_break (0);
359  return;
360  }
361 
362  if (record_count > 64 * 1024)
363  {
364  /* sanity check, don't stack allocate far too much just
365  because database might contain a large value here */
366  GNUNET_break (0);
368  return;
369  }
370 
371  {
372  struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (record_count)];
373 
374  GNUNET_assert (0 != serial);
375  if (GNUNET_OK !=
377  data,
378  record_count,
379  rd))
380  {
381  GNUNET_break (0);
383  return;
384  }
385  pc->iter (pc->iter_cls,
386  serial,
387  (NULL == pc->zone_key) ? &zk : pc->zone_key,
388  label,
389  record_count,
390  rd);
391  }
393  }
394  pc->limit -= num_results;
395 }
396 
397 
408 static int
410  const struct
412  const char *label,
414  void *iter_cls)
415 {
416  struct Plugin *plugin = cls;
417  struct GNUNET_PQ_QueryParam params[] = {
421  };
422  struct ParserContext pc;
424 
425  if (NULL == zone)
426  {
427  GNUNET_break (0);
428  return GNUNET_SYSERR;
429  }
430  pc.iter = iter;
431  pc.iter_cls = iter_cls;
432  pc.zone_key = zone;
434  "lookup_label",
435  params,
437  &pc);
438  if (res < 0)
439  return GNUNET_SYSERR;
441  return GNUNET_NO;
442  return GNUNET_OK;
443 }
444 
445 
458 static int
460  const struct
462  uint64_t serial,
463  uint64_t limit,
465  void *iter_cls)
466 {
467  struct Plugin *plugin = cls;
469  struct ParserContext pc;
470 
471  pc.iter = iter;
472  pc.iter_cls = iter_cls;
473  pc.zone_key = zone;
474  pc.limit = limit;
475  if (NULL == zone)
476  {
477  struct GNUNET_PQ_QueryParam params_without_zone[] = {
481  };
482 
484  "iterate_all_zones",
485  params_without_zone,
487  &pc);
488  }
489  else
490  {
491  struct GNUNET_PQ_QueryParam params_with_zone[] = {
496  };
497 
499  "iterate_zone",
500  params_with_zone,
502  &pc);
503  }
504  if (res < 0)
505  return GNUNET_SYSERR;
506 
508  (pc.limit > 0))
509  return GNUNET_NO;
510  return GNUNET_OK;
511 }
512 
513 
525 static int
527  const struct
529  const struct
530  GNUNET_IDENTITY_PublicKey *value_zone,
532  void *iter_cls)
533 {
534  struct Plugin *plugin = cls;
535  struct GNUNET_PQ_QueryParam params[] = {
539  };
541  struct ParserContext pc;
542 
543  pc.iter = iter;
544  pc.iter_cls = iter_cls;
545  pc.zone_key = zone;
547  "zone_to_name",
548  params,
550  &pc);
551  if (res < 0)
552  return GNUNET_SYSERR;
553  return GNUNET_OK;
554 }
555 
556 
563 static void
565 {
566  GNUNET_PQ_disconnect (plugin->dbh);
567  plugin->dbh = NULL;
568 }
569 
570 
577 void *
579 {
580  static struct Plugin plugin;
581  const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
583 
584  if (NULL != plugin.cfg)
585  return NULL; /* can only initialize once! */
586  memset (&plugin, 0, sizeof(struct Plugin));
587  plugin.cfg = cfg;
588  if (GNUNET_OK != database_setup (&plugin))
589  {
590  database_shutdown (&plugin);
591  return NULL;
592  }
594  api->cls = &plugin;
600  "Postgres namestore plugin running\n");
601  return api;
602 }
603 
604 
611 void *
613 {
615  struct Plugin *plugin = api->cls;
616 
617  database_shutdown (plugin);
618  plugin->cfg = NULL;
619  GNUNET_free (api);
621  "Postgres namestore plugin is finished\n");
622  return NULL;
623 }
624 
625 
626 /* end of plugin_namestore_postgres.c */
void GNUNET_PQ_disconnect(struct GNUNET_PQ_Context *db)
Disconnect from the database, destroying the prepared statements and releasing other associated resou...
Definition: pq_connect.c:516
GNUNET_NETWORK_STRUCT_END ssize_t GNUNET_GNSRECORD_records_get_size(unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd)
Calculate how many bytes we will need to serialize the given records.
struct GNUNET_PQ_ExecuteStatement GNUNET_PQ_make_execute(const char *sql)
Create a struct GNUNET_PQ_ExecuteStatement where errors are fatal.
Definition: pq_exec.c:36
uint64_t GNUNET_CRYPTO_random_u64(enum GNUNET_CRYPTO_Quality mode, uint64_t max)
Random on unsigned 64-bit values.
ssize_t GNUNET_GNSRECORD_records_serialize(unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd, size_t dest_size, char *dest)
Serialize the given records to the given destination buffer.
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_string(const char *ptr)
Generate query parameter for a string.
enum GNUNET_DB_QueryStatus GNUNET_PQ_eval_prepared_multi_select(struct GNUNET_PQ_Context *db, const char *statement_name, const struct GNUNET_PQ_QueryParam *params, GNUNET_PQ_PostgresResultHandler rh, void *rh_cls)
Execute a named prepared statement that is a SELECT statement which may return multiple results in co...
Definition: pq_eval.c:209
static size_t data_size
Number of bytes in data.
Definition: gnunet-abd.c:187
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
Information needed to run a list of SQL statements using GNUNET_PQ_exec_statements().
static int namestore_postgres_iterate_records(void *cls, const struct GNUNET_IDENTITY_PrivateKey *zone, uint64_t serial, uint64_t limit, GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
Iterate over the results for a particular key and zone in the datastore.
Information needed to prepare a list of SQL statements using GNUNET_PQ_prepare_statements().
const struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
Description of a DB result cell.
A private key for an identity as per LSD0001.
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
static void database_shutdown(struct Plugin *plugin)
Shutdown database connection and associate data structures.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
static int namestore_postgres_store_records(void *cls, const struct GNUNET_IDENTITY_PrivateKey *zone_key, const char *label, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd)
Store a record in the datastore.
int GNUNET_GNSRECORD_records_deserialize(size_t len, const char *src, unsigned int rd_count, struct GNUNET_GNSRECORD_Data *dest)
Deserialize the given records to the given destination.
#define LOG(kind,...)
uint64_t limit
Number of results still to return (counted down by number of results given to iterator).
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
struct GNUNET_BLOCK_PluginFunctions * api
Plugin API.
Definition: block.c:47
common internal definitions for namestore service
static char * zone
Name of the zone we manage.
void * cls
Closure to pass to all plugin functions.
static uint64_t record_count
Record count.
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_string(const char *name, char **dst)
0-terminated string expected.
#define GNUNET_PQ_result_spec_end
End of result parameter specification.
#define GNUNET_PQ_query_param_end
End of query parameter specification.
Definition: gnunet_pq_lib.h:97
void * libgnunet_plugin_namestore_postgres_init(void *cls)
Entry point for the plugin.
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_variable_size(const char *name, void **dst, size_t *sptr)
Variable-size result expected.
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_uint32(const char *name, uint32_t *u32)
uint32_t expected.
enum GNUNET_GenericReturnValue GNUNET_GNSRECORD_is_zonekey_type(uint32_t type)
Check if this type is one of the supported GNS zone types.
#define GNUNET_PQ_query_param_auto_from_type(x)
Generate fixed-size query parameter with size determined by variable type.
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_uint32(const uint32_t *x)
Generate query parameter for an uint32_t in host byte order.
const struct GNUNET_IDENTITY_PrivateKey * zone_key
Zone key, NULL if part of record.
static int database_setup(struct Plugin *plugin)
Initialize the database connections and associated data structures (create tables and indices as need...
enum GNUNET_GenericReturnValue GNUNET_GNSRECORD_identity_from_data(const char *data, size_t data_size, uint32_t type, struct GNUNET_IDENTITY_PublicKey *key)
Build a #GNUNET_GNSRECORD_PublicKey from zone delegation resource record data.
static int res
static char * plugin
Solver plugin name as string.
struct GNUNET_PQ_Context * GNUNET_PQ_connect_with_cfg(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *load_path_suffix, const struct GNUNET_PQ_ExecuteStatement *es, const struct GNUNET_PQ_PreparedStatement *ps)
Connect to a postgres database using the configuration option "CONFIG" in section.
Definition: pq_connect.c:469
#define GNUNET_PQ_result_spec_auto_from_type(name, dst)
We expect a fixed-size result, with size determined by the type of * dst
static int namestore_postgres_zone_to_name(void *cls, const struct GNUNET_IDENTITY_PrivateKey *zone, const struct GNUNET_IDENTITY_PublicKey *value_zone, GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
Look for an existing PKEY delegation record for a given public key.
#define GNUNET_PQ_PREPARED_STATEMENT_END
Terminator for prepared statement list.
GNUNET_NAMESTORE_RecordIterator iter
Function to call for each result.
int(* lookup_records)(void *cls, const struct GNUNET_IDENTITY_PrivateKey *zone, const char *label, GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
Lookup records in the datastore for which we are the authority.
static void parse_result_call_iterator(void *cls, PGresult *res, unsigned int num_results)
A statement has been run.
int(* zone_to_name)(void *cls, const struct GNUNET_IDENTITY_PrivateKey *zone, const struct GNUNET_IDENTITY_PublicKey *value_zone, GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
Look for an existing PKEY delegation record for a given public key.
helper functions for Postgres DB interactions
void * iter_cls
Iterator cls.
static struct GNUNET_FS_SearchContext * sc
Definition: gnunet-search.c:37
void * libgnunet_plugin_namestore_postgres_done(void *cls)
Exit point from the plugin.
Closure for parse_result_call_iterator.
The transaction succeeded, and yielded one result.
Definition: gnunet_db_lib.h:58
enum GNUNET_DB_QueryStatus GNUNET_PQ_eval_prepared_non_select(struct GNUNET_PQ_Context *db, const char *statement_name, const struct GNUNET_PQ_QueryParam *params)
Execute a named prepared statement that is NOT a SELECT statement in connection using the given param...
Definition: pq_eval.c:164
static struct GNUNET_FS_PublishContext * pc
Handle to FS-publishing operation.
int(* store_records)(void *cls, const struct GNUNET_IDENTITY_PrivateKey *zone, const char *label, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd)
Store a record in the datastore for which we are the authority.
An identity key as per LSD0001.
configuration data
Definition: configuration.c:84
Handle for a plugin.
Definition: block.c:37
struct GNUNET_PQ_Context * dbh
Native Postgres database handle.
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_uint64(const uint64_t *x)
Generate query parameter for an uint16_t in host byte order.
void * iter_cls
Closure for iter.
Description of a DB query parameter.
Definition: gnunet_pq_lib.h:64
void(* GNUNET_NAMESTORE_RecordIterator)(void *cls, uint64_t serial, const struct GNUNET_IDENTITY_PrivateKey *private_key, const char *label, unsigned int rd_count, const struct GNUNET_GNSRECORD_Data *rd)
Function called for each matching record.
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_fixed_size(const void *ptr, size_t ptr_size)
Generate query parameter for a buffer ptr of ptr_size bytes.
static int namestore_postgres_lookup_records(void *cls, const struct GNUNET_IDENTITY_PrivateKey *zone, const char *label, GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
Lookup records in the datastore for which we are the authority.
#define GNUNET_PQ_EXECUTE_STATEMENT_END
Terminator for executable statement list.
GNUNET_DB_QueryStatus
Status code returned from functions running database commands.
Definition: gnunet_db_lib.h:34
struct GNUNET_PQ_ExecuteStatement GNUNET_PQ_make_try_execute(const char *sql)
Create a struct GNUNET_PQ_ExecuteStatement where errors should be tolerated.
Definition: pq_exec.c:55
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_uint64(const char *name, uint64_t *u64)
uint64_t expected.
int(* iterate_records)(void *cls, const struct GNUNET_IDENTITY_PrivateKey *zone, uint64_t serial, uint64_t limit, GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
Iterate over the results for a particular zone in the datastore.
#define GNUNET_log_from(kind, comp,...)
The transaction succeeded, but yielded zero results.
Definition: gnunet_db_lib.h:53
int GNUNET_CONFIGURATION_get_value_yesno(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option)
Get a configuration value that should be in a set of "YES" or "NO".
uint32_t data
The data value.
GNUNET_PEERSTORE_Processor iter
Iterator.
struct returned by the initialization function of the plugin
Handle to Postgres database.
Definition: pq.h:34
struct GNUNET_PQ_PreparedStatement GNUNET_PQ_make_prepare(const char *name, const char *sql, unsigned int num_args)
Create a struct GNUNET_PQ_PreparedStatement.
Definition: pq_prepare.c:38
No good quality of the operation is needed (i.e., random numbers can be pseudo-random).
int GNUNET_PQ_extract_result(PGresult *result, struct GNUNET_PQ_ResultSpec *rs, int row)
Extract results from a query result according to the given specification.
Definition: pq.c:127
#define GNUNET_free(ptr)
Wrapper around free.
void GNUNET_PQ_cleanup_result(struct GNUNET_PQ_ResultSpec *rs)
Free all memory that was allocated in rs during GNUNET_PQ_extract_result().
Definition: pq.c:117