aboutsummaryrefslogtreecommitdiff
path: root/src/plugin/namestore/plugin_namestore_postgres.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugin/namestore/plugin_namestore_postgres.c')
-rw-r--r--src/plugin/namestore/plugin_namestore_postgres.c795
1 files changed, 795 insertions, 0 deletions
diff --git a/src/plugin/namestore/plugin_namestore_postgres.c b/src/plugin/namestore/plugin_namestore_postgres.c
new file mode 100644
index 000000000..1dc526dc2
--- /dev/null
+++ b/src/plugin/namestore/plugin_namestore_postgres.c
@@ -0,0 +1,795 @@
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
21/**
22 * @file namestore/plugin_namestore_postgres.c
23 * @brief postgres-based namestore backend
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_namestore_plugin.h"
28#include "gnunet_namestore_service.h"
29#include "gnunet_gnsrecord_lib.h"
30#include "gnunet_pq_lib.h"
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "namestore-postgres", __VA_ARGS__)
34
35
36/**
37 * Context for all functions in this plugin.
38 */
39struct Plugin
40{
41 /**
42 * Our configuration.
43 */
44 const struct GNUNET_CONFIGURATION_Handle *cfg;
45
46 /**
47 * Postgres database handle.
48 */
49 struct GNUNET_PQ_Context *dbh;
50
51 /**
52 * Database is prepared and ready
53 */
54 bool ready;
55};
56
57
58/**
59 * Initialize the database connections and associated data structures (create
60 * tables and indices as needed as well).
61 *
62 * @param cls the plugin context (state for this module)
63 * @return #GNUNET_OK on success
64 */
65static enum GNUNET_GenericReturnValue
66namestore_postgres_create_tables (void *cls)
67{
68 struct Plugin *plugin = cls;
69 struct GNUNET_PQ_Context *dbh;
70
71 dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
72 "namestore-postgres",
73 "namestore-",
74 NULL,
75 NULL);
76 if (NULL == dbh)
77 return GNUNET_SYSERR;
78 GNUNET_PQ_disconnect (dbh);
79 return GNUNET_OK;
80}
81
82
83/**
84 * Drop existing namestore tables.
85 *
86 * @param cls the plugin context (state for this module)
87 * @return #GNUNET_OK on success
88 */
89static enum GNUNET_GenericReturnValue
90namestore_postgres_drop_tables (void *cls)
91{
92 struct Plugin *plugin = cls;
93 struct GNUNET_PQ_Context *dbh;
94 enum GNUNET_GenericReturnValue ret;
95
96 dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
97 "namestore-postgres",
98 NULL,
99 NULL,
100 NULL);
101 if (NULL == dbh)
102 {
103 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
104 "Failed to connect to database\n");
105 return GNUNET_SYSERR;
106 }
107 ret = GNUNET_PQ_exec_sql (dbh,
108 "namestore-drop");
109 GNUNET_PQ_disconnect (dbh);
110 return ret;
111}
112
113
114static enum GNUNET_GenericReturnValue
115database_prepare (struct Plugin *plugin)
116{
117 enum GNUNET_GenericReturnValue ret;
118
119 if (plugin->ready)
120 return GNUNET_OK;
121 {
122 struct GNUNET_PQ_PreparedStatement ps[] = {
123 GNUNET_PQ_make_prepare ("store_records",
124 "INSERT INTO namestore.ns098records"
125 " (zone_private_key, pkey, rvalue, record_count, record_data, label)"
126 " VALUES ($1, $2, $3, $4, $5, $6)"
127 " ON CONFLICT ON CONSTRAINT zl"
128 " DO UPDATE"
129 " SET pkey=$2,rvalue=$3,record_count=$4,record_data=$5"
130 " WHERE ns098records.zone_private_key = $1"
131 " AND ns098records.label = $6"),
132 GNUNET_PQ_make_prepare ("delete_records",
133 "DELETE FROM namestore.ns098records "
134 "WHERE zone_private_key=$1 AND label=$2"),
135 GNUNET_PQ_make_prepare ("zone_to_name",
136 "SELECT seq,record_count,record_data,label FROM namestore.ns098records"
137 " WHERE zone_private_key=$1 AND pkey=$2"),
138 GNUNET_PQ_make_prepare ("iterate_zone",
139 "SELECT seq,record_count,record_data,label FROM namestore.ns098records "
140 "WHERE zone_private_key=$1 AND seq > $2 ORDER BY seq ASC LIMIT $3"),
141 GNUNET_PQ_make_prepare ("iterate_all_zones",
142 "SELECT seq,record_count,record_data,label,zone_private_key"
143 " FROM namestore.ns098records WHERE seq > $1 ORDER BY seq ASC LIMIT $2"),
144 GNUNET_PQ_make_prepare ("lookup_label",
145 "SELECT seq,record_count,record_data,label "
146 "FROM namestore.ns098records WHERE zone_private_key=$1 AND label=$2"),
147 GNUNET_PQ_make_prepare ("edit_set",
148 "SELECT seq,record_count,record_data,label "
149 "FROM namestore.ns098records WHERE zone_private_key=$1 AND label=$2 FOR UPDATE NOWAIT"),
150 GNUNET_PQ_PREPARED_STATEMENT_END
151 };
152
153 ret = GNUNET_PQ_prepare_statements (plugin->dbh,
154 ps);
155 }
156 if (GNUNET_OK != ret)
157 return ret;
158 plugin->ready = true;
159 return GNUNET_OK;
160}
161
162
163/**
164 * Initialize the database connections and associated
165 * data structures (create tables and indices
166 * as needed as well).
167 *
168 * @param plugin the plugin context (state for this module)
169 * @return #GNUNET_OK on success
170 */
171static enum GNUNET_GenericReturnValue
172database_connect (struct Plugin *plugin)
173{
174 struct GNUNET_PQ_ExecuteStatement ess[] = {
175 GNUNET_PQ_make_try_execute ("SET synchronous_commit TO off"),
176 GNUNET_PQ_EXECUTE_STATEMENT_END
177 };
178 struct GNUNET_PQ_ExecuteStatement *es;
179
180 if (GNUNET_YES ==
181 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
182 "namestore-postgres",
183 "ASYNC_COMMIT"))
184 es = &ess[0];
185 else
186 es = &ess[1];
187
188 if (GNUNET_YES ==
189 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
190 "namestore-postgres",
191 "INIT_ON_CONNECT"))
192 {
193 if (GNUNET_OK !=
194 namestore_postgres_create_tables (plugin))
195 {
196 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
197 "Failed to create tables\n");
198 return GNUNET_SYSERR;
199 }
200 }
201 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
202 "namestore-postgres",
203 NULL,
204 es,
205 NULL);
206 if (NULL == plugin->dbh)
207 return GNUNET_SYSERR;
208 return GNUNET_OK;
209}
210
211
212/**
213 * Store a record in the datastore. Removes any existing record in the
214 * same zone with the same name.
215 *
216 * @param cls closure (internal context for the plugin)
217 * @param zone_key private key of the zone
218 * @param label name that is being mapped (at most 255 characters long)
219 * @param rd_count number of entries in @a rd array
220 * @param rd array of records with data to store
221 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
222 */
223static enum GNUNET_GenericReturnValue
224namestore_postgres_store_records (void *cls,
225 const struct
226 GNUNET_CRYPTO_PrivateKey *zone_key,
227 const char *label,
228 unsigned int rd_count,
229 const struct GNUNET_GNSRECORD_Data *rd)
230{
231 struct Plugin *plugin = cls;
232 struct GNUNET_CRYPTO_PublicKey pkey;
233 uint64_t rvalue;
234 uint32_t rd_count32 = (uint32_t) rd_count;
235 ssize_t data_size;
236
237 GNUNET_assert (GNUNET_OK == database_prepare (plugin));
238 memset (&pkey,
239 0,
240 sizeof(pkey));
241 for (unsigned int i = 0; i < rd_count; i++)
242 if (GNUNET_YES ==
243 GNUNET_GNSRECORD_is_zonekey_type (rd[i].record_type))
244 {
245 GNUNET_break (GNUNET_OK ==
246 GNUNET_GNSRECORD_identity_from_data (rd[i].data,
247 rd[i].data_size,
248 rd[i].record_type,
249 &pkey));
250 break;
251 }
252 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
253 UINT64_MAX);
254 data_size = GNUNET_GNSRECORD_records_get_size (rd_count,
255 rd);
256 if (data_size < 0)
257 {
258 GNUNET_break (0);
259 return GNUNET_SYSERR;
260 }
261 if (data_size >= UINT16_MAX)
262 {
263 GNUNET_break (0);
264 return GNUNET_SYSERR;
265 }
266 /* if record set is empty, delete existing records */
267 if (0 == rd_count)
268 {
269 struct GNUNET_PQ_QueryParam params[] = {
270 GNUNET_PQ_query_param_auto_from_type (zone_key),
271 GNUNET_PQ_query_param_string (label),
272 GNUNET_PQ_query_param_end
273 };
274 enum GNUNET_DB_QueryStatus res;
275
276 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
277 "delete_records",
278 params);
279 if ((GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != res) &&
280 (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != res))
281 {
282 GNUNET_break (0);
283 return GNUNET_SYSERR;
284 }
285 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
286 "postgres",
287 "Record deleted\n");
288 return GNUNET_OK;
289 }
290 /* otherwise, UPSERT (i.e. UPDATE if exists, otherwise INSERT) */
291 {
292 char data[data_size];
293 struct GNUNET_PQ_QueryParam params[] = {
294 GNUNET_PQ_query_param_auto_from_type (zone_key),
295 GNUNET_PQ_query_param_auto_from_type (&pkey),
296 GNUNET_PQ_query_param_uint64 (&rvalue),
297 GNUNET_PQ_query_param_uint32 (&rd_count32),
298 GNUNET_PQ_query_param_fixed_size (data, data_size),
299 GNUNET_PQ_query_param_string (label),
300 GNUNET_PQ_query_param_end
301 };
302 enum GNUNET_DB_QueryStatus res;
303 ssize_t ret;
304
305 ret = GNUNET_GNSRECORD_records_serialize (rd_count,
306 rd,
307 data_size,
308 data);
309 if ((ret < 0) ||
310 (data_size != ret))
311 {
312 GNUNET_break (0);
313 return GNUNET_SYSERR;
314 }
315
316 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
317 "store_records",
318 params);
319 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != res)
320 return GNUNET_SYSERR;
321 }
322 return GNUNET_OK;
323}
324
325
326/**
327 * Closure for #parse_result_call_iterator.
328 */
329struct ParserContext
330{
331 /**
332 * Function to call for each result.
333 */
334 GNUNET_NAMESTORE_RecordIterator iter;
335
336 /**
337 * Closure for @e iter.
338 */
339 void *iter_cls;
340
341 /**
342 * Zone key, NULL if part of record.
343 */
344 const struct GNUNET_CRYPTO_PrivateKey *zone_key;
345
346 /**
347 * Number of results still to return (counted down by
348 * number of results given to iterator).
349 */
350 uint64_t limit;
351};
352
353
354/**
355 * A statement has been run. We should evaluate the result, and if possible
356 * call the @a iter in @a cls with the result.
357 *
358 * @param cls closure of type `struct ParserContext *`
359 * @param res the postgres result
360 * @param num_results the number of results in @a result
361 */
362static void
363parse_result_call_iterator (void *cls,
364 PGresult *res,
365 unsigned int num_results)
366{
367 struct ParserContext *pc = cls;
368
369 if (NULL == pc->iter)
370 return; /* no need to do more work */
371 LOG (GNUNET_ERROR_TYPE_DEBUG,
372 "Got %d results from PQ.\n", num_results);
373 for (unsigned int i = 0; i < num_results; i++)
374 {
375 uint64_t serial;
376 void *data;
377 size_t data_size;
378 uint32_t record_count;
379 char *label;
380 struct GNUNET_CRYPTO_PrivateKey zk;
381 struct GNUNET_PQ_ResultSpec rs_with_zone[] = {
382 GNUNET_PQ_result_spec_uint64 ("seq", &serial),
383 GNUNET_PQ_result_spec_uint32 ("record_count", &record_count),
384 GNUNET_PQ_result_spec_variable_size ("record_data", &data, &data_size),
385 GNUNET_PQ_result_spec_string ("label", &label),
386 GNUNET_PQ_result_spec_auto_from_type ("zone_private_key", &zk),
387 GNUNET_PQ_result_spec_end
388 };
389 struct GNUNET_PQ_ResultSpec rs_without_zone[] = {
390 GNUNET_PQ_result_spec_uint64 ("seq", &serial),
391 GNUNET_PQ_result_spec_uint32 ("record_count", &record_count),
392 GNUNET_PQ_result_spec_variable_size ("record_data", &data, &data_size),
393 GNUNET_PQ_result_spec_string ("label", &label),
394 GNUNET_PQ_result_spec_end
395 };
396 struct GNUNET_PQ_ResultSpec *rs;
397
398 rs = (NULL == pc->zone_key) ? rs_with_zone : rs_without_zone;
399 if (GNUNET_YES !=
400 GNUNET_PQ_extract_result (res,
401 rs,
402 i))
403 {
404 GNUNET_break (0);
405 return;
406 }
407
408 if (record_count > 64 * 1024)
409 {
410 /* sanity check, don't stack allocate far too much just
411 because database might contain a large value here */
412 GNUNET_break (0);
413 GNUNET_PQ_cleanup_result (rs);
414 return;
415 }
416
417 {
418 struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (record_count)];
419
420 GNUNET_assert (0 != serial);
421 if (GNUNET_OK !=
422 GNUNET_GNSRECORD_records_deserialize (data_size,
423 data,
424 record_count,
425 rd))
426 {
427 GNUNET_break (0);
428 GNUNET_PQ_cleanup_result (rs);
429 return;
430 }
431 pc->iter (pc->iter_cls,
432 serial,
433 (NULL == pc->zone_key) ? &zk : pc->zone_key,
434 label,
435 record_count,
436 rd);
437 }
438 GNUNET_PQ_cleanup_result (rs);
439 }
440 pc->limit -= num_results;
441}
442
443
444/**
445 * Lookup records in the datastore for which we are the authority.
446 *
447 * @param cls closure (internal context for the plugin)
448 * @param zone private key of the zone
449 * @param label name of the record in the zone
450 * @param iter function to call with the result
451 * @param iter_cls closure for @a iter
452 * @param method the method to use "lookup_record" or "edit_set"
453 * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR
454 */
455static enum GNUNET_GenericReturnValue
456lookup_records (void *cls,
457 const struct
458 GNUNET_CRYPTO_PrivateKey *zone,
459 const char *label,
460 GNUNET_NAMESTORE_RecordIterator iter,
461 void *iter_cls,
462 const char*method)
463{
464 struct Plugin *plugin = cls;
465 GNUNET_assert (GNUNET_OK == database_prepare (plugin));
466 struct GNUNET_PQ_QueryParam params[] = {
467 GNUNET_PQ_query_param_auto_from_type (zone),
468 GNUNET_PQ_query_param_string (label),
469 GNUNET_PQ_query_param_end
470 };
471 struct ParserContext pc;
472 enum GNUNET_DB_QueryStatus res;
473
474 if (NULL == zone)
475 {
476 GNUNET_break (0);
477 return GNUNET_SYSERR;
478 }
479 pc.iter = iter;
480 pc.iter_cls = iter_cls;
481 pc.zone_key = zone;
482 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
483 method,
484 params,
485 &parse_result_call_iterator,
486 &pc);
487 if (res < 0)
488 return GNUNET_SYSERR;
489 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res)
490 return GNUNET_NO;
491 return GNUNET_OK;
492}
493
494
495/**
496 * Lookup records in the datastore for which we are the authority.
497 *
498 * @param cls closure (internal context for the plugin)
499 * @param zone private key of the zone
500 * @param label name of the record in the zone
501 * @param iter function to call with the result
502 * @param iter_cls closure for @a iter
503 * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR
504 */
505static enum GNUNET_GenericReturnValue
506namestore_postgres_lookup_records (void *cls,
507 const struct
508 GNUNET_CRYPTO_PrivateKey *zone,
509 const char *label,
510 GNUNET_NAMESTORE_RecordIterator iter,
511 void *iter_cls)
512{
513 return lookup_records (cls, zone, label, iter, iter_cls, "lookup_label");
514}
515
516
517/**
518 * Edit records in the datastore for which we are the authority.
519 *
520 * @param cls closure (internal context for the plugin)
521 * @param zone private key of the zone
522 * @param label name of the record in the zone
523 * @param iter function to call with the result
524 * @param iter_cls closure for @a iter
525 * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR
526 */
527static int
528namestore_postgres_edit_records (void *cls,
529 const struct
530 GNUNET_CRYPTO_PrivateKey *zone,
531 const char *label,
532 GNUNET_NAMESTORE_RecordIterator iter,
533 void *iter_cls)
534{
535 return lookup_records (cls, zone, label, iter, iter_cls, "edit_set");
536}
537
538
539/**
540 * Iterate over the results for a particular key and zone in the
541 * datastore. Will return 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, NULL to iterate over all zones
545 * @param serial serial number to exclude in the list of all matching records
546 * @param limit maximum number of results to fetch
547 * @param iter function to call with the result
548 * @param iter_cls closure for @a iter
549 * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error
550 */
551static enum GNUNET_GenericReturnValue
552namestore_postgres_iterate_records (void *cls,
553 const struct
554 GNUNET_CRYPTO_PrivateKey *zone,
555 uint64_t serial,
556 uint64_t limit,
557 GNUNET_NAMESTORE_RecordIterator iter,
558 void *iter_cls)
559{
560 struct Plugin *plugin = cls;
561 enum GNUNET_DB_QueryStatus res;
562 struct ParserContext pc;
563
564 GNUNET_assert (GNUNET_OK == database_prepare (plugin));
565 pc.iter = iter;
566 pc.iter_cls = iter_cls;
567 pc.zone_key = zone;
568 pc.limit = limit;
569 if (NULL == zone)
570 {
571 struct GNUNET_PQ_QueryParam params_without_zone[] = {
572 GNUNET_PQ_query_param_uint64 (&serial),
573 GNUNET_PQ_query_param_uint64 (&limit),
574 GNUNET_PQ_query_param_end
575 };
576
577 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
578 "iterate_all_zones",
579 params_without_zone,
580 &parse_result_call_iterator,
581 &pc);
582 }
583 else
584 {
585 struct GNUNET_PQ_QueryParam params_with_zone[] = {
586 GNUNET_PQ_query_param_auto_from_type (zone),
587 GNUNET_PQ_query_param_uint64 (&serial),
588 GNUNET_PQ_query_param_uint64 (&limit),
589 GNUNET_PQ_query_param_end
590 };
591
592 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
593 "iterate_zone",
594 params_with_zone,
595 &parse_result_call_iterator,
596 &pc);
597 }
598 if (res < 0)
599 return GNUNET_SYSERR;
600
601 if ((GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res) ||
602 (pc.limit > 0))
603 return GNUNET_NO;
604 return GNUNET_OK;
605}
606
607
608/**
609 * Look for an existing PKEY delegation record for a given public key.
610 * Returns at most one result to the iterator.
611 *
612 * @param cls closure (internal context for the plugin)
613 * @param zone private key of the zone to look up in, never NULL
614 * @param value_zone public key of the target zone (value), never NULL
615 * @param iter function to call with the result
616 * @param iter_cls closure for @a iter
617 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
618 */
619static enum GNUNET_GenericReturnValue
620namestore_postgres_zone_to_name (void *cls,
621 const struct
622 GNUNET_CRYPTO_PrivateKey *zone,
623 const struct
624 GNUNET_CRYPTO_PublicKey *value_zone,
625 GNUNET_NAMESTORE_RecordIterator iter,
626 void *iter_cls)
627{
628 struct Plugin *plugin = cls;
629 GNUNET_assert (GNUNET_OK == database_prepare (plugin));
630 struct GNUNET_PQ_QueryParam params[] = {
631 GNUNET_PQ_query_param_auto_from_type (zone),
632 GNUNET_PQ_query_param_auto_from_type (value_zone),
633 GNUNET_PQ_query_param_end
634 };
635 enum GNUNET_DB_QueryStatus res;
636 struct ParserContext pc;
637
638 pc.iter = iter;
639 pc.iter_cls = iter_cls;
640 pc.zone_key = zone;
641 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
642 "zone_to_name",
643 params,
644 &parse_result_call_iterator,
645 &pc);
646 if (res < 0)
647 return GNUNET_SYSERR;
648 return GNUNET_OK;
649}
650
651
652/**
653 * Begin a transaction for a client.
654 *
655 * @param cls closure (internal context for the plugin)
656 * @param emsg error message set of return code is #GNUNET_SYSERR
657 * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started.
658 */
659static enum GNUNET_GenericReturnValue
660namestore_postgres_transaction_begin (void *cls,
661 char **emsg)
662{
663 struct Plugin *plugin = cls;
664 GNUNET_assert (GNUNET_OK == database_prepare (plugin));
665 struct GNUNET_PQ_ExecuteStatement es[] = {
666 GNUNET_PQ_make_execute ("BEGIN"),
667 GNUNET_PQ_EXECUTE_STATEMENT_END
668 };
669
670 return GNUNET_PQ_exec_statements (plugin->dbh, es);
671}
672
673
674/**
675 * Commit a transaction for a client.
676 * This releases the lock on the database.
677 *
678 * @param cls closure (internal context for the plugin)
679 * @param emsg error message set of return code is #GNUNET_SYSERR
680 * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started.
681 */
682static enum GNUNET_GenericReturnValue
683namestore_postgres_transaction_rollback (void *cls,
684 char **emsg)
685{
686 struct Plugin *plugin = cls;
687 GNUNET_assert (GNUNET_OK == database_prepare (plugin));
688 struct GNUNET_PQ_ExecuteStatement es[] = {
689 GNUNET_PQ_make_execute ("ROLLBACK"),
690 GNUNET_PQ_EXECUTE_STATEMENT_END
691 };
692
693 return GNUNET_PQ_exec_statements (plugin->dbh, es);
694}
695
696
697/**
698 * Roll back a transaction for a client.
699 * This releases the lock on the database.
700 *
701 * @param cls closure (internal context for the plugin)
702 * @param emsg error message set of return code is #GNUNET_SYSERR
703 * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started.
704 */
705static enum GNUNET_GenericReturnValue
706namestore_postgres_transaction_commit (void *cls,
707 char **emsg)
708{
709 struct Plugin *plugin = cls;
710 GNUNET_assert (GNUNET_OK == database_prepare (plugin));
711 struct GNUNET_PQ_ExecuteStatement es[] = {
712 GNUNET_PQ_make_execute ("COMMIT"),
713 GNUNET_PQ_EXECUTE_STATEMENT_END
714 };
715
716 return GNUNET_PQ_exec_statements (plugin->dbh, es);
717}
718
719
720/**
721 * Shutdown database connection and associate data
722 * structures.
723 *
724 * @param plugin the plugin context (state for this module)
725 */
726static void
727database_shutdown (struct Plugin *plugin)
728{
729 GNUNET_PQ_disconnect (plugin->dbh);
730 plugin->dbh = NULL;
731}
732
733
734/**
735 * Entry point for the plugin.
736 *
737 * @param cls the `struct GNUNET_NAMESTORE_PluginEnvironment*`
738 * @return NULL on error, othrewise the plugin context
739 */
740void *
741libgnunet_plugin_namestore_postgres_init (void *cls)
742{
743 struct Plugin *plugin;
744 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
745 struct GNUNET_NAMESTORE_PluginFunctions *api;
746
747 plugin = GNUNET_new (struct Plugin);
748 plugin->cfg = cfg;
749 if (GNUNET_OK != database_connect (plugin))
750 {
751 database_shutdown (plugin);
752 GNUNET_free (plugin);
753 return NULL;
754 }
755 api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions);
756 api->cls = plugin;
757 api->create_tables = &namestore_postgres_create_tables;
758 api->drop_tables = &namestore_postgres_drop_tables;
759 api->store_records = &namestore_postgres_store_records;
760 api->iterate_records = &namestore_postgres_iterate_records;
761 api->zone_to_name = &namestore_postgres_zone_to_name;
762 api->lookup_records = &namestore_postgres_lookup_records;
763 api->transaction_begin = &namestore_postgres_transaction_begin;
764 api->transaction_commit = &namestore_postgres_transaction_commit;
765 api->transaction_rollback = &namestore_postgres_transaction_rollback;
766 api->edit_records = &namestore_postgres_edit_records;
767 LOG (GNUNET_ERROR_TYPE_INFO,
768 "Postgres namestore plugin running\n");
769 return api;
770}
771
772
773/**
774 * Exit point from the plugin.
775 *
776 * @param cls the plugin context (as returned by "init")
777 * @return always NULL
778 */
779void *
780libgnunet_plugin_namestore_postgres_done (void *cls)
781{
782 struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
783 struct Plugin *plugin = api->cls;
784
785 database_shutdown (plugin);
786 plugin->cfg = NULL;
787 GNUNET_free (plugin);
788 GNUNET_free (api);
789 LOG (GNUNET_ERROR_TYPE_DEBUG,
790 "Postgres namestore plugin is finished\n");
791 return NULL;
792}
793
794
795/* end of plugin_namestore_postgres.c */