GNUnet  0.20.0
plugin_datacache_postgres.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet
3  Copyright (C) 2006, 2009, 2010, 2012, 2015, 2017, 2018, 2022 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"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_pq_lib.h"
30 
31 #define LOG(kind, ...) GNUNET_log_from (kind, "datacache-postgres", __VA_ARGS__)
32 
36 #define OVERHEAD (sizeof(struct GNUNET_HashCode) + 24)
37 
41 struct Plugin
42 {
47 
52 
56  unsigned int num_items;
57 };
58 
59 
66 static enum GNUNET_GenericReturnValue
68 {
69  struct GNUNET_PQ_PreparedStatement ps[] = {
70  GNUNET_PQ_make_prepare ("getkt",
71  "SELECT expiration_time,type,ro,value,trunc,path"
72  " FROM datacache.gn180dc"
73  " WHERE key=$1 AND type=$2 AND expiration_time >= $3"),
74  GNUNET_PQ_make_prepare ("getk",
75  "SELECT expiration_time,type,ro,value,trunc,path"
76  " FROM datacache.gn180dc"
77  " WHERE key=$1 AND expiration_time >= $2"),
78  GNUNET_PQ_make_prepare ("getex",
79  "SELECT LENGTH(value) AS len,oid,key"
80  " FROM datacache.gn180dc"
81  " WHERE expiration_time < $1"
82  " ORDER BY expiration_time ASC LIMIT 1"),
83  GNUNET_PQ_make_prepare ("getm",
84  "SELECT LENGTH(value) AS len,oid,key"
85  " FROM datacache.gn180dc"
86  " ORDER BY prox ASC, expiration_time ASC LIMIT 1"),
87  GNUNET_PQ_make_prepare ("get_closest",
88  "(SELECT expiration_time,type,ro,value,trunc,path,key"
89  " FROM datacache.gn180dc"
90  " WHERE key >= $1"
91  " AND expiration_time >= $2"
92  " AND ( (type = $3) OR ( 0 = $3) )"
93  " ORDER BY key ASC"
94  " LIMIT $4)"
95  " UNION "
96  "(SELECT expiration_time,type,ro,value,trunc,path,key"
97  " FROM datacache.gn180dc"
98  " WHERE key <= $1"
99  " AND expiration_time >= $2"
100  " AND ( (type = $3) OR ( 0 = $3) )"
101  " ORDER BY key DESC"
102  " LIMIT $4)"),
103  GNUNET_PQ_make_prepare ("delrow",
104  "DELETE FROM datacache.gn180dc"
105  " WHERE oid=$1"),
106  GNUNET_PQ_make_prepare ("put",
107  "INSERT INTO datacache.gn180dc"
108  " (type, ro, prox, expiration_time, key, value, trunc, path) "
109  "VALUES ($1, $2, $3, $4, $5, $6, $7, $8)"),
111  };
112 
113  plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg,
114  "datacache-postgres",
115  "datacache-",
116  NULL,
117  ps);
118  if (NULL == plugin->dbh)
119  return GNUNET_SYSERR;
120  return GNUNET_OK;
121 }
122 
123 
132 static ssize_t
134  uint32_t prox,
135  const struct GNUNET_DATACACHE_Block *block)
136 {
137  struct Plugin *plugin = cls;
138  uint32_t type32 = (uint32_t) block->type;
139  uint32_t ro32 = (uint32_t) block->type;
140  struct GNUNET_PQ_QueryParam params[] = {
147  block->data_size),
149  (0 == block->put_path_length)
152  block->put_path,
153  block->put_path_length
154  * sizeof(struct GNUNET_DHT_PathElement)),
156  };
158 
160  "put",
161  params);
162  if (0 > ret)
163  return -1;
164  plugin->num_items++;
165  return block->data_size + OVERHEAD;
166 }
167 
168 
173 {
178 
182  void *iter_cls;
183 
187  const struct GNUNET_HashCode *key;
188 };
189 
190 
200 static void
201 handle_results (void *cls,
202  PGresult *result,
203  unsigned int num_results)
204 {
205  struct HandleResultContext *hrc = cls;
206 
207  for (unsigned int i = 0; i < num_results; i++)
208  {
209  uint32_t type32;
210  uint32_t bro32;
211  void *data;
212  struct GNUNET_DATACACHE_Block block;
213  void *path = NULL;
214  size_t path_size = 0;
215  struct GNUNET_PQ_ResultSpec rs[] = {
216  GNUNET_PQ_result_spec_absolute_time ("expiration_time",
217  &block.expiration_time),
219  &type32),
221  &bro32),
223  &data,
224  &block.data_size),
226  &block.trunc_peer),
229  &path,
230  &path_size),
231  NULL),
233  };
234 
235  if (GNUNET_YES !=
237  rs,
238  i))
239  {
240  GNUNET_break (0);
241  return;
242  }
243  if (0 != (path_size % sizeof(struct GNUNET_DHT_PathElement)))
244  {
245  GNUNET_break (0);
246  path_size = 0;
247  path = NULL;
248  }
249  block.data = data;
250  block.put_path = path;
251  block.put_path_length
252  = path_size / sizeof (struct GNUNET_DHT_PathElement);
253  block.type = (enum GNUNET_BLOCK_Type) type32;
254  block.ro = (enum GNUNET_DHT_RouteOption) bro32;
255  block.key = *hrc->key;
257  "Found result of size %u bytes and type %u in database\n",
258  (unsigned int) block.data_size,
259  (unsigned int) block.type);
260  if ( (NULL != hrc->iter) &&
261  (GNUNET_SYSERR ==
262  hrc->iter (hrc->iter_cls,
263  &block)) )
264  {
266  "Ending iteration (client error)\n");
268  return;
269  }
271  }
272 }
273 
274 
286 static unsigned int
288  const struct GNUNET_HashCode *key,
289  enum GNUNET_BLOCK_Type type,
291  void *iter_cls)
292 {
293  struct Plugin *plugin = cls;
294  uint32_t type32 = (uint32_t) type;
295  struct GNUNET_TIME_Absolute now = { 0 };
296  struct GNUNET_PQ_QueryParam paramk[] = {
300  };
301  struct GNUNET_PQ_QueryParam paramkt[] = {
306  };
308  struct HandleResultContext hr_ctx;
309 
310  now = GNUNET_TIME_absolute_get ();
311  hr_ctx.iter = iter;
312  hr_ctx.iter_cls = iter_cls;
313  hr_ctx.key = key;
315  (0 == type) ? "getk" : "getkt",
316  (0 == type) ? paramk : paramkt,
318  &hr_ctx);
319  if (res < 0)
320  return 0;
321  return res;
322 }
323 
324 
332 static enum GNUNET_GenericReturnValue
333 postgres_plugin_del (void *cls)
334 {
335  struct Plugin *plugin = cls;
336  struct GNUNET_PQ_QueryParam pempty[] = {
338  };
339  uint32_t size;
340  uint64_t oid;
341  struct GNUNET_HashCode key;
342  struct GNUNET_PQ_ResultSpec rs[] = {
344  &size),
346  &oid),
348  &key),
350  };
352  struct GNUNET_PQ_QueryParam dparam[] = {
355  };
356  struct GNUNET_TIME_Absolute now;
357  struct GNUNET_PQ_QueryParam xparam[] = {
360  };
361 
362  now = GNUNET_TIME_absolute_get ();
364  "getex",
365  xparam,
366  rs);
367  if (0 >= res)
369  "getm",
370  pempty,
371  rs);
372  if (0 > res)
373  return GNUNET_SYSERR;
375  {
376  /* no result */
378  "Ending iteration (no more results)\n");
379  return 0;
380  }
382  "delrow",
383  dparam);
384  if (0 > res)
385  {
387  return GNUNET_SYSERR;
388  }
389  plugin->num_items--;
390  plugin->env->delete_notify (plugin->env->cls,
391  &key,
392  size + OVERHEAD);
394  return GNUNET_OK;
395 }
396 
397 
402 {
407 
411  void *iter_cls;
412 };
413 
414 
424 static void
425 extract_result_cb (void *cls,
426  PGresult *result,
427  unsigned int num_results)
428 {
429  struct ExtractResultContext *erc = cls;
430 
431  if (NULL == erc->iter)
432  return;
433  for (unsigned int i = 0; i < num_results; i++)
434  {
435  uint32_t type32;
436  uint32_t bro32;
437  struct GNUNET_DATACACHE_Block block;
438  void *data;
439  void *path;
440  size_t path_size;
441  struct GNUNET_PQ_ResultSpec rs[] = {
442  GNUNET_PQ_result_spec_absolute_time ("expiration_time",
443  &block.expiration_time),
445  &type32),
447  &bro32),
449  &data,
450  &block.data_size),
452  &block.trunc_peer),
454  &path,
455  &path_size),
457  &block.key),
459  };
460 
461  if (GNUNET_YES !=
463  rs,
464  i))
465  {
466  GNUNET_break (0);
467  return;
468  }
469  if (0 != (path_size % sizeof(struct GNUNET_DHT_PathElement)))
470  {
471  GNUNET_break (0);
472  path_size = 0;
473  path = NULL;
474  }
475  block.type = (enum GNUNET_BLOCK_Type) type32;
476  block.ro = (enum GNUNET_DHT_RouteOption) bro32;
477  block.data = data;
478  block.put_path = path;
479  block.put_path_length = path_size / sizeof (struct GNUNET_DHT_PathElement);
481  "Found result of size %u bytes and type %u in database\n",
482  (unsigned int) block.data_size,
483  (unsigned int) block.type);
484  if ( (NULL != erc->iter) &&
485  (GNUNET_SYSERR ==
486  erc->iter (erc->iter_cls,
487  &block)) )
488  {
490  "Ending iteration (client error)\n");
492  break;
493  }
495  }
496 }
497 
498 
513 static unsigned int
515  const struct GNUNET_HashCode *key,
516  enum GNUNET_BLOCK_Type type,
517  unsigned int num_results,
519  void *iter_cls)
520 {
521  struct Plugin *plugin = cls;
522  uint32_t num_results32 = (uint32_t) num_results;
523  uint32_t type32 = (uint32_t) type;
524  struct GNUNET_TIME_Absolute now;
525  struct GNUNET_PQ_QueryParam params[] = {
529  GNUNET_PQ_query_param_uint32 (&num_results32),
531  };
533  struct ExtractResultContext erc;
534 
535  erc.iter = iter;
536  erc.iter_cls = iter_cls;
537  now = GNUNET_TIME_absolute_get ();
539  "get_closest",
540  params,
542  &erc);
543  if (0 > res)
544  {
546  "Ending iteration (postgres error)\n");
547  return 0;
548  }
550  {
551  /* no result */
553  "Ending iteration (no more results)\n");
554  return 0;
555  }
556  return res;
557 }
558 
559 
566 void *
568 {
571  struct Plugin *plugin;
572 
573  plugin = GNUNET_new (struct Plugin);
574  plugin->env = env;
575 
577  {
579  return NULL;
580  }
581 
583  api->cls = plugin;
584  api->get = &postgres_plugin_get;
585  api->put = &postgres_plugin_put;
586  api->del = &postgres_plugin_del;
587  api->get_closest = &postgres_plugin_get_closest;
589  "Postgres datacache running\n");
590  return api;
591 }
592 
593 
600 void *
602 {
604  struct Plugin *plugin = api->cls;
605 
608  "datacache-drop"));
611  GNUNET_free (api);
612  return NULL;
613 }
614 
615 
616 /* end of plugin_datacache_postgres.c */
struct GNUNET_MQ_Envelope * env
Definition: 005.c:1
GNUNET_BLOCK_Type
WARNING: This header is generated! In order to add DHT block types, you must register them in GANA,...
static int ret
Return value of the commandline.
Definition: gnunet-abd.c:81
static int res
struct TestcasePlugin * plugin
The process handle to the testbed service.
struct GNUNET_HashCode key
The key used in the DHT.
uint32_t data
The data value.
static int result
Global testing status.
API for database backends for the datacache.
GNUNET_DB_QueryStatus
Status code returned from functions running database commands.
Definition: gnunet_db_lib.h:37
@ GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
The transaction succeeded, but yielded zero results.
Definition: gnunet_db_lib.h:55
helper functions for Postgres DB interactions
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.
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_uint64(const uint64_t *x)
Generate query parameter for an uint16_t in host byte order.
#define GNUNET_PQ_query_param_auto_from_type(x)
Generate fixed-size query parameter with size determined by variable type.
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:165
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_uint32(const char *name, uint32_t *u32)
uint32_t expected.
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_null(void)
Generate query parameter to create a NULL value.
enum GNUNET_DB_QueryStatus GNUNET_PQ_eval_prepared_singleton_select(struct GNUNET_PQ_Context *db, const char *statement_name, const struct GNUNET_PQ_QueryParam *params, struct GNUNET_PQ_ResultSpec *rs)
Execute a named prepared statement that is a SELECT statement which must return a single result in co...
Definition: pq_eval.c:199
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_absolute_time(const struct GNUNET_TIME_Absolute *x)
Generate query parameter for an absolute time value.
#define GNUNET_PQ_result_spec_auto_from_type(name, dst)
We expect a fixed-size result, with size determined by the type of * dst
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_uint64(const char *name, uint64_t *u64)
uint64_t expected.
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:684
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_allow_null(struct GNUNET_PQ_ResultSpec rs, bool *is_null)
Allow NULL value to be found in the database for the given value.
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_absolute_time(const char *name, struct GNUNET_TIME_Absolute *at)
Absolute time expected.
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:619
struct GNUNET_PQ_PreparedStatement GNUNET_PQ_make_prepare(const char *name, const char *sql)
Create a struct GNUNET_PQ_PreparedStatement.
Definition: pq_prepare.c:30
#define GNUNET_PQ_query_param_end
End of query parameter specification.
struct GNUNET_PQ_QueryParam GNUNET_PQ_query_param_uint32(const uint32_t *x)
Generate query parameter for an uint32_t in host byte order.
struct GNUNET_PQ_ResultSpec GNUNET_PQ_result_spec_variable_size(const char *name, void **dst, size_t *sptr)
Variable-size result expected.
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:139
#define GNUNET_PQ_PREPARED_STATEMENT_END
Terminator for prepared statement list.
enum GNUNET_GenericReturnValue 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:149
uint32_t oid
Definition: gnunet_pq_lib.h:2
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:135
enum GNUNET_GenericReturnValue GNUNET_PQ_exec_sql(struct GNUNET_PQ_Context *db, const char *buf)
Execute SQL statements from buf against db.
Definition: pq_connect.c:144
#define GNUNET_PQ_result_spec_end
End of result parameter specification.
enum GNUNET_GenericReturnValue(* GNUNET_DATACACHE_Iterator)(void *cls, const struct GNUNET_DATACACHE_Block *block)
An iterator over a set of items stored in the datacache.
GNUNET_DHT_RouteOption
Options for routing.
GNUNET_GenericReturnValue
Named constants for return values.
@ GNUNET_OK
@ GNUNET_YES
@ GNUNET_SYSERR
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
@ GNUNET_ERROR_TYPE_DEBUG
@ GNUNET_ERROR_TYPE_INFO
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_free(ptr)
Wrapper around free.
struct GNUNET_TIME_Absolute GNUNET_TIME_absolute_get(void)
Get the current time.
Definition: time.c:111
static unsigned int size
Size of the "table".
Definition: peer.c:68
#define OVERHEAD
Per-entry overhead estimate.
static ssize_t postgres_plugin_put(void *cls, uint32_t prox, const struct GNUNET_DATACACHE_Block *block)
Store an item in the datastore.
void * libgnunet_plugin_datacache_postgres_done(void *cls)
Exit point from the plugin.
static enum GNUNET_GenericReturnValue postgres_plugin_del(void *cls)
Delete the entry with the lowest expiration value from the datacache right now.
static enum GNUNET_GenericReturnValue init_connection(struct Plugin *plugin)
Get a database handle.
static unsigned int postgres_plugin_get(void *cls, const struct GNUNET_HashCode *key, enum GNUNET_BLOCK_Type type, GNUNET_DATACACHE_Iterator iter, void *iter_cls)
Iterate over the results for a particular key in the datastore.
static unsigned int postgres_plugin_get_closest(void *cls, const struct GNUNET_HashCode *key, enum GNUNET_BLOCK_Type type, unsigned int num_results, GNUNET_DATACACHE_Iterator iter, void *iter_cls)
Iterate over the results that are "close" to a particular key in the datacache.
void * libgnunet_plugin_datacache_postgres_init(void *cls)
Entry point for the plugin.
static void extract_result_cb(void *cls, PGresult *result, unsigned int num_results)
Function to be called with the results of a SELECT statement that has returned num_results results.
#define LOG(kind,...)
static void handle_results(void *cls, PGresult *result, unsigned int num_results)
Function to be called with the results of a SELECT statement that has returned num_results results.
Closure for extract_result_cb.
void * iter_cls
Closure for iter.
GNUNET_DATACACHE_Iterator iter
Function to call for each result found.
void * cls
Closure for all of the callbacks.
Information about a block stored in the datacache.
const struct GNUNET_DHT_PathElement * put_path
PUT path taken by the block, array of peer identities.
enum GNUNET_BLOCK_Type type
Type of the block.
const void * data
Actual block data.
enum GNUNET_DHT_RouteOption ro
Options for routing for the block.
struct GNUNET_PeerIdentity trunc_peer
If the path was truncated, this is the peer ID at which the path was truncated.
struct GNUNET_HashCode key
Key of the block.
size_t data_size
Number of bytes in data.
unsigned int put_path_length
Length of the put_path array.
struct GNUNET_TIME_Absolute expiration_time
When does the block expire?
The datastore service will pass a pointer to a struct of this type as the first and only argument to ...
void * cls
Closure to use for callbacks.
struct returned by the initialization function of the plugin
void * cls
Closure to pass to all plugin functions.
A (signed) path tracking a block's flow through the DHT is represented by an array of path elements,...
A 512-bit hashcode.
Handle to Postgres database.
Definition: pq.h:36
Information needed to prepare a list of SQL statements using GNUNET_PQ_prepare_statements().
Description of a DB query parameter.
Definition: gnunet_pq_lib.h:83
Description of a DB result cell.
Time for absolute times used by GNUnet, in microseconds.
Closure for handle_results.
void * iter_cls
Closure for iter.
const struct GNUNET_HashCode * key
Key used.
GNUNET_DATACACHE_Iterator iter
Function to call on each result, may be NULL.
Handle for a plugin.
Definition: block.c:38
struct GNUNET_BLOCK_PluginFunctions * api
Plugin API.
Definition: block.c:47
unsigned int num_items
Number of key-value pairs in the database.
struct GNUNET_DATACACHE_PluginEnvironment * env
Our execution environment.
struct GNUNET_PQ_Context * dbh
Native Postgres database handle.
enum GNUNET_TESTBED_UnderlayLinkModelType type
the type of this model