aboutsummaryrefslogtreecommitdiff
path: root/src/datacache/plugin_datacache_postgres.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/datacache/plugin_datacache_postgres.c')
-rw-r--r--src/datacache/plugin_datacache_postgres.c634
1 files changed, 0 insertions, 634 deletions
diff --git a/src/datacache/plugin_datacache_postgres.c b/src/datacache/plugin_datacache_postgres.c
deleted file mode 100644
index b1f9a1b8e..000000000
--- a/src/datacache/plugin_datacache_postgres.c
+++ /dev/null
@@ -1,634 +0,0 @@
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
21/**
22 * @file datacache/plugin_datacache_postgres.c
23 * @brief postgres for an implementation of a database backend for the datacache
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_util_lib.h"
28#include "gnunet_pq_lib.h"
29#include "gnunet_datacache_plugin.h"
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "datacache-postgres", __VA_ARGS__)
32
33/**
34 * Per-entry overhead estimate
35 */
36#define OVERHEAD (sizeof(struct GNUNET_HashCode) + 24)
37
38/**
39 * Context for all functions in this plugin.
40 */
41struct Plugin
42{
43 /**
44 * Our execution environment.
45 */
46 struct GNUNET_DATACACHE_PluginEnvironment *env;
47
48 /**
49 * Native Postgres database handle.
50 */
51 struct GNUNET_PQ_Context *dbh;
52
53 /**
54 * Number of key-value pairs in the database.
55 */
56 unsigned int num_items;
57};
58
59
60/**
61 * @brief Get a database handle
62 *
63 * @param plugin global context
64 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
65 */
66static enum GNUNET_GenericReturnValue
67init_connection (struct Plugin *plugin)
68{
69 struct GNUNET_PQ_ExecuteStatement es[] = {
70 GNUNET_PQ_make_try_execute (
71 "CREATE TEMPORARY SEQUENCE IF NOT EXISTS gn180dc_oid_seq"),
72 GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS gn180dc ("
73 " oid OID NOT NULL DEFAULT nextval('gn180dc_oid_seq'),"
74 " type INT4 NOT NULL,"
75 " ro INT4 NOT NULL,"
76 " prox INT4 NOT NULL,"
77 " expiration_time INT8 NOT NULL,"
78 " key BYTEA NOT NULL CHECK(LENGTH(key)=64),"
79 " trunc BYTEA NOT NULL CHECK(LENGTH(trunc)=32),"
80 " value BYTEA NOT NULL,"
81 " path BYTEA DEFAULT NULL)"),
82 GNUNET_PQ_make_try_execute (
83 "ALTER SEQUENCE gnu011dc_oid_seq OWNED BY gn180dc.oid"),
84 GNUNET_PQ_make_try_execute (
85 "CREATE INDEX IF NOT EXISTS idx_oid ON gn180dc (oid)"),
86 GNUNET_PQ_make_try_execute (
87 "CREATE INDEX IF NOT EXISTS idx_key ON gn180dc (key)"),
88 GNUNET_PQ_make_try_execute (
89 "CREATE INDEX IF NOT EXISTS idx_dt ON gn180dc (expiration_time)"),
90 GNUNET_PQ_make_execute (
91 "ALTER TABLE gn180dc ALTER value SET STORAGE EXTERNAL"),
92 GNUNET_PQ_make_execute ("ALTER TABLE gn180dc ALTER key SET STORAGE PLAIN"),
93 GNUNET_PQ_EXECUTE_STATEMENT_END
94 };
95 struct GNUNET_PQ_PreparedStatement ps[] = {
96 GNUNET_PQ_make_prepare ("getkt",
97 "SELECT expiration_time,type,ro,value,trunc,path FROM gn180dc "
98 "WHERE key=$1 AND type=$2 AND expiration_time >= $3",
99 3),
100 GNUNET_PQ_make_prepare ("getk",
101 "SELECT expiration_time,type,ro,value,trunc,path FROM gn180dc "
102 "WHERE key=$1 AND expiration_time >= $2",
103 2),
104 GNUNET_PQ_make_prepare ("getex",
105 "SELECT LENGTH(value) AS len,oid,key FROM gn180dc"
106 " WHERE expiration_time < $1"
107 " ORDER BY expiration_time ASC LIMIT 1",
108 1),
109 GNUNET_PQ_make_prepare ("getm",
110 "SELECT LENGTH(value) AS len,oid,key FROM gn180dc"
111 " ORDER BY prox ASC, expiration_time ASC LIMIT 1",
112 0),
113 GNUNET_PQ_make_prepare ("get_closest",
114 "(SELECT expiration_time,type,ro,value,trunc,path,key FROM gn180dc"
115 " WHERE key >= $1"
116 " AND expiration_time >= $2"
117 " AND ( (type = $3) OR ( 0 = $3) )"
118 " ORDER BY key ASC"
119 " LIMIT $4)"
120 " UNION "
121 "(SELECT expiration_time,type,ro,value,trunc,path,key FROM gn180dc"
122 " WHERE key <= $1"
123 " AND expiration_time >= $2"
124 " AND ( (type = $3) OR ( 0 = $3) )"
125 " ORDER BY key DESC"
126 " LIMIT $4)",
127 4),
128 GNUNET_PQ_make_prepare ("delrow",
129 "DELETE FROM gn180dc WHERE oid=$1",
130 1),
131 GNUNET_PQ_make_prepare ("put",
132 "INSERT INTO gn180dc"
133 " (type, ro, prox, expiration_time, key, value, trunc, path) "
134 "VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
135 8),
136 GNUNET_PQ_PREPARED_STATEMENT_END
137 };
138
139 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg,
140 "datacache-postgres",
141 NULL,
142 es,
143 ps);
144 if (NULL == plugin->dbh)
145 return GNUNET_SYSERR;
146 return GNUNET_OK;
147}
148
149
150/**
151 * Store an item in the datastore.
152 *
153 * @param cls closure (our `struct Plugin`)
154 * @param prox proximity of @a key to my PID
155 * @param block data to store
156 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
157 */
158static ssize_t
159postgres_plugin_put (void *cls,
160 uint32_t prox,
161 const struct GNUNET_DATACACHE_Block *block)
162{
163 struct Plugin *plugin = cls;
164 uint32_t type32 = (uint32_t) block->type;
165 uint32_t ro32 = (uint32_t) block->type;
166 struct GNUNET_PQ_QueryParam params[] = {
167 GNUNET_PQ_query_param_uint32 (&type32),
168 GNUNET_PQ_query_param_uint32 (&ro32),
169 GNUNET_PQ_query_param_uint32 (&prox),
170 GNUNET_PQ_query_param_absolute_time (&block->expiration_time),
171 GNUNET_PQ_query_param_auto_from_type (&block->key),
172 GNUNET_PQ_query_param_fixed_size (block->data,
173 block->data_size),
174 GNUNET_PQ_query_param_auto_from_type (&block->trunc_peer),
175 GNUNET_PQ_query_param_fixed_size (block->put_path,
176 block->put_path_length
177 * sizeof(struct GNUNET_DHT_PathElement)),
178 GNUNET_PQ_query_param_end
179 };
180 enum GNUNET_DB_QueryStatus ret;
181
182 ret = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
183 "put",
184 params);
185 if (0 > ret)
186 return -1;
187 plugin->num_items++;
188 return block->data_size + OVERHEAD;
189}
190
191
192/**
193 * Closure for #handle_results.
194 */
195struct HandleResultContext
196{
197 /**
198 * Function to call on each result, may be NULL.
199 */
200 GNUNET_DATACACHE_Iterator iter;
201
202 /**
203 * Closure for @e iter.
204 */
205 void *iter_cls;
206
207 /**
208 * Key used.
209 */
210 const struct GNUNET_HashCode *key;
211};
212
213
214/**
215 * Function to be called with the results of a SELECT statement
216 * that has returned @a num_results results. Parse the result
217 * and call the callback given in @a cls
218 *
219 * @param cls closure of type `struct HandleResultContext`
220 * @param result the postgres result
221 * @param num_result the number of results in @a result
222 */
223static void
224handle_results (void *cls,
225 PGresult *result,
226 unsigned int num_results)
227{
228 struct HandleResultContext *hrc = cls;
229
230 for (unsigned int i = 0; i < num_results; i++)
231 {
232 uint32_t type32;
233 uint32_t bro32;
234 void *data;
235 struct GNUNET_DATACACHE_Block block;
236 void *path;
237 size_t path_size;
238 struct GNUNET_PQ_ResultSpec rs[] = {
239 GNUNET_PQ_result_spec_absolute_time ("expiration_time",
240 &block.expiration_time),
241 GNUNET_PQ_result_spec_uint32 ("type",
242 &type32),
243 GNUNET_PQ_result_spec_uint32 ("ro",
244 &bro32),
245 GNUNET_PQ_result_spec_variable_size ("value",
246 &data,
247 &block.data_size),
248 GNUNET_PQ_result_spec_auto_from_type ("trunc",
249 &block.trunc_peer),
250 GNUNET_PQ_result_spec_variable_size ("path",
251 &path,
252 &path_size),
253 GNUNET_PQ_result_spec_end
254 };
255
256 if (GNUNET_YES !=
257 GNUNET_PQ_extract_result (result,
258 rs,
259 i))
260 {
261 GNUNET_break (0);
262 return;
263 }
264 if (0 != (path_size % sizeof(struct GNUNET_DHT_PathElement)))
265 {
266 GNUNET_break (0);
267 path_size = 0;
268 path = NULL;
269 }
270 block.data = data;
271 block.put_path = path;
272 block.put_path_length
273 = path_size / sizeof (struct GNUNET_DHT_PathElement);
274 block.type = (enum GNUNET_BLOCK_Type) type32;
275 block.ro = (enum GNUNET_DHT_RouteOption) bro32;
276 block.key = *hrc->key;
277 LOG (GNUNET_ERROR_TYPE_DEBUG,
278 "Found result of size %u bytes and type %u in database\n",
279 (unsigned int) block.data_size,
280 (unsigned int) block.type);
281 if ( (NULL != hrc->iter) &&
282 (GNUNET_SYSERR ==
283 hrc->iter (hrc->iter_cls,
284 &block)) )
285 {
286 LOG (GNUNET_ERROR_TYPE_DEBUG,
287 "Ending iteration (client error)\n");
288 GNUNET_PQ_cleanup_result (rs);
289 return;
290 }
291 GNUNET_PQ_cleanup_result (rs);
292 }
293}
294
295
296/**
297 * Iterate over the results for a particular key
298 * in the datastore.
299 *
300 * @param cls closure (our `struct Plugin`)
301 * @param key key to look for
302 * @param type entries of which type are relevant?
303 * @param iter maybe NULL (to just count)
304 * @param iter_cls closure for @a iter
305 * @return the number of results found
306 */
307static unsigned int
308postgres_plugin_get (void *cls,
309 const struct GNUNET_HashCode *key,
310 enum GNUNET_BLOCK_Type type,
311 GNUNET_DATACACHE_Iterator iter,
312 void *iter_cls)
313{
314 struct Plugin *plugin = cls;
315 uint32_t type32 = (uint32_t) type;
316 struct GNUNET_TIME_Absolute now = { 0 };
317 struct GNUNET_PQ_QueryParam paramk[] = {
318 GNUNET_PQ_query_param_auto_from_type (key),
319 GNUNET_PQ_query_param_absolute_time (&now),
320 GNUNET_PQ_query_param_end
321 };
322 struct GNUNET_PQ_QueryParam paramkt[] = {
323 GNUNET_PQ_query_param_auto_from_type (key),
324 GNUNET_PQ_query_param_uint32 (&type32),
325 GNUNET_PQ_query_param_absolute_time (&now),
326 GNUNET_PQ_query_param_end
327 };
328 enum GNUNET_DB_QueryStatus res;
329 struct HandleResultContext hr_ctx;
330
331 now = GNUNET_TIME_absolute_get ();
332 hr_ctx.iter = iter;
333 hr_ctx.iter_cls = iter_cls;
334 hr_ctx.key = key;
335 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
336 (0 == type) ? "getk" : "getkt",
337 (0 == type) ? paramk : paramkt,
338 &handle_results,
339 &hr_ctx);
340 if (res < 0)
341 return 0;
342 return res;
343}
344
345
346/**
347 * Delete the entry with the lowest expiration value
348 * from the datacache right now.
349 *
350 * @param cls closure (our `struct Plugin`)
351 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
352 */
353static enum GNUNET_GenericReturnValue
354postgres_plugin_del (void *cls)
355{
356 struct Plugin *plugin = cls;
357 struct GNUNET_PQ_QueryParam pempty[] = {
358 GNUNET_PQ_query_param_end
359 };
360 uint32_t size;
361 uint32_t oid;
362 struct GNUNET_HashCode key;
363 struct GNUNET_PQ_ResultSpec rs[] = {
364 GNUNET_PQ_result_spec_uint32 ("len",
365 &size),
366 GNUNET_PQ_result_spec_uint32 ("oid",
367 &oid),
368 GNUNET_PQ_result_spec_auto_from_type ("key",
369 &key),
370 GNUNET_PQ_result_spec_end
371 };
372 enum GNUNET_DB_QueryStatus res;
373 struct GNUNET_PQ_QueryParam dparam[] = {
374 GNUNET_PQ_query_param_uint32 (&oid),
375 GNUNET_PQ_query_param_end
376 };
377 struct GNUNET_TIME_Absolute now;
378 struct GNUNET_PQ_QueryParam xparam[] = {
379 GNUNET_PQ_query_param_absolute_time (&now),
380 GNUNET_PQ_query_param_end
381 };
382
383 now = GNUNET_TIME_absolute_get ();
384 res = GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh,
385 "getex",
386 xparam,
387 rs);
388 if (0 >= res)
389 res = GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh,
390 "getm",
391 pempty,
392 rs);
393 if (0 > res)
394 return GNUNET_SYSERR;
395 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res)
396 {
397 /* no result */
398 LOG (GNUNET_ERROR_TYPE_DEBUG,
399 "Ending iteration (no more results)\n");
400 return 0;
401 }
402 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
403 "delrow",
404 dparam);
405 if (0 > res)
406 {
407 GNUNET_PQ_cleanup_result (rs);
408 return GNUNET_SYSERR;
409 }
410 plugin->num_items--;
411 plugin->env->delete_notify (plugin->env->cls,
412 &key,
413 size + OVERHEAD);
414 GNUNET_PQ_cleanup_result (rs);
415 return GNUNET_OK;
416}
417
418
419/**
420 * Closure for #extract_result_cb.
421 */
422struct ExtractResultContext
423{
424 /**
425 * Function to call for each result found.
426 */
427 GNUNET_DATACACHE_Iterator iter;
428
429 /**
430 * Closure for @e iter.
431 */
432 void *iter_cls;
433};
434
435
436/**
437 * Function to be called with the results of a SELECT statement
438 * that has returned @a num_results results. Calls the `iter`
439 * from @a cls for each result.
440 *
441 * @param cls closure with the `struct ExtractResultContext`
442 * @param result the postgres result
443 * @param num_result the number of results in @a result
444 */
445static void
446extract_result_cb (void *cls,
447 PGresult *result,
448 unsigned int num_results)
449{
450 struct ExtractResultContext *erc = cls;
451
452 if (NULL == erc->iter)
453 return;
454 for (unsigned int i = 0; i < num_results; i++)
455 {
456 uint32_t type32;
457 uint32_t bro32;
458 struct GNUNET_DATACACHE_Block block;
459 void *data;
460 void *path;
461 size_t path_size;
462 struct GNUNET_PQ_ResultSpec rs[] = {
463 GNUNET_PQ_result_spec_absolute_time ("expiration_time",
464 &block.expiration_time),
465 GNUNET_PQ_result_spec_uint32 ("type",
466 &type32),
467 GNUNET_PQ_result_spec_uint32 ("ro",
468 &bro32),
469 GNUNET_PQ_result_spec_variable_size ("value",
470 &data,
471 &block.data_size),
472 GNUNET_PQ_result_spec_auto_from_type ("trunc",
473 &block.trunc_peer),
474 GNUNET_PQ_result_spec_variable_size ("path",
475 &path,
476 &path_size),
477 GNUNET_PQ_result_spec_auto_from_type ("key",
478 &block.key),
479 GNUNET_PQ_result_spec_end
480 };
481
482 if (GNUNET_YES !=
483 GNUNET_PQ_extract_result (result,
484 rs,
485 i))
486 {
487 GNUNET_break (0);
488 return;
489 }
490 if (0 != (path_size % sizeof(struct GNUNET_DHT_PathElement)))
491 {
492 GNUNET_break (0);
493 path_size = 0;
494 path = NULL;
495 }
496 block.type = (enum GNUNET_BLOCK_Type) type32;
497 block.ro = (enum GNUNET_DHT_RouteOption) bro32;
498 block.data = data;
499 block.put_path = path;
500 block.put_path_length = path_size / sizeof (struct GNUNET_DHT_PathElement);
501 LOG (GNUNET_ERROR_TYPE_DEBUG,
502 "Found result of size %u bytes and type %u in database\n",
503 (unsigned int) block.data_size,
504 (unsigned int) block.type);
505 if ( (NULL != erc->iter) &&
506 (GNUNET_SYSERR ==
507 erc->iter (erc->iter_cls,
508 &block)) )
509 {
510 LOG (GNUNET_ERROR_TYPE_DEBUG,
511 "Ending iteration (client error)\n");
512 GNUNET_PQ_cleanup_result (rs);
513 break;
514 }
515 GNUNET_PQ_cleanup_result (rs);
516 }
517}
518
519
520/**
521 * Iterate over the results that are "close" to a particular key in
522 * the datacache. "close" is defined as numerically larger than @a
523 * key (when interpreted as a circular address space), with small
524 * distance.
525 *
526 * @param cls closure (internal context for the plugin)
527 * @param key area of the keyspace to look into
528 * @param type desired block type for the replies
529 * @param num_results number of results that should be returned to @a iter
530 * @param iter maybe NULL (to just count)
531 * @param iter_cls closure for @a iter
532 * @return the number of results found
533 */
534static unsigned int
535postgres_plugin_get_closest (void *cls,
536 const struct GNUNET_HashCode *key,
537 enum GNUNET_BLOCK_Type type,
538 unsigned int num_results,
539 GNUNET_DATACACHE_Iterator iter,
540 void *iter_cls)
541{
542 struct Plugin *plugin = cls;
543 uint32_t num_results32 = (uint32_t) num_results;
544 uint32_t type32 = (uint32_t) type;
545 struct GNUNET_TIME_Absolute now;
546 struct GNUNET_PQ_QueryParam params[] = {
547 GNUNET_PQ_query_param_auto_from_type (key),
548 GNUNET_PQ_query_param_absolute_time (&now),
549 GNUNET_PQ_query_param_uint32 (&type32),
550 GNUNET_PQ_query_param_uint32 (&num_results32),
551 GNUNET_PQ_query_param_end
552 };
553 enum GNUNET_DB_QueryStatus res;
554 struct ExtractResultContext erc;
555
556 erc.iter = iter;
557 erc.iter_cls = iter_cls;
558 now = GNUNET_TIME_absolute_get ();
559 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
560 "get_closest",
561 params,
562 &extract_result_cb,
563 &erc);
564 if (0 > res)
565 {
566 LOG (GNUNET_ERROR_TYPE_DEBUG,
567 "Ending iteration (postgres error)\n");
568 return 0;
569 }
570 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res)
571 {
572 /* no result */
573 LOG (GNUNET_ERROR_TYPE_DEBUG,
574 "Ending iteration (no more results)\n");
575 return 0;
576 }
577 return res;
578}
579
580
581/**
582 * Entry point for the plugin.
583 *
584 * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironmnet`)
585 * @return the plugin's closure (our `struct Plugin`)
586 */
587void *
588libgnunet_plugin_datacache_postgres_init (void *cls)
589{
590 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
591 struct GNUNET_DATACACHE_PluginFunctions *api;
592 struct Plugin *plugin;
593
594 plugin = GNUNET_new (struct Plugin);
595 plugin->env = env;
596
597 if (GNUNET_OK != init_connection (plugin))
598 {
599 GNUNET_free (plugin);
600 return NULL;
601 }
602
603 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
604 api->cls = plugin;
605 api->get = &postgres_plugin_get;
606 api->put = &postgres_plugin_put;
607 api->del = &postgres_plugin_del;
608 api->get_closest = &postgres_plugin_get_closest;
609 LOG (GNUNET_ERROR_TYPE_INFO,
610 "Postgres datacache running\n");
611 return api;
612}
613
614
615/**
616 * Exit point from the plugin.
617 *
618 * @param cls closure (our `struct Plugin`)
619 * @return NULL
620 */
621void *
622libgnunet_plugin_datacache_postgres_done (void *cls)
623{
624 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
625 struct Plugin *plugin = api->cls;
626
627 GNUNET_PQ_disconnect (plugin->dbh);
628 GNUNET_free (plugin);
629 GNUNET_free (api);
630 return NULL;
631}
632
633
634/* end of plugin_datacache_postgres.c */