aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/datacache/plugin_datacache_postgres.c4
-rw-r--r--src/datastore/plugin_datastore_postgres.c3
-rw-r--r--src/include/gnunet_pq_lib.h293
-rw-r--r--src/namecache/plugin_namecache_postgres.c152
-rw-r--r--src/namestore/plugin_namestore_postgres.c200
-rw-r--r--src/postgres/postgres.c42
-rw-r--r--src/pq/Makefile.am4
-rw-r--r--src/pq/pq_connect.c127
-rw-r--r--src/pq/pq_eval.c249
-rw-r--r--src/pq/pq_exec.c106
-rw-r--r--src/pq/pq_prepare.c92
-rw-r--r--src/psycstore/plugin_psycstore_postgres.c219
12 files changed, 1144 insertions, 347 deletions
diff --git a/src/datacache/plugin_datacache_postgres.c b/src/datacache/plugin_datacache_postgres.c
index 13c2c26a2..8f5cdbde1 100644
--- a/src/datacache/plugin_datacache_postgres.c
+++ b/src/datacache/plugin_datacache_postgres.c
@@ -68,8 +68,8 @@ init_connection (struct Plugin *plugin)
68{ 68{
69 PGresult *ret; 69 PGresult *ret;
70 70
71 plugin->dbh = GNUNET_POSTGRES_connect (plugin->env->cfg, 71 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg,
72 "datacache-postgres"); 72 "datacache-postgres");
73 if (NULL == plugin->dbh) 73 if (NULL == plugin->dbh)
74 return GNUNET_SYSERR; 74 return GNUNET_SYSERR;
75 ret = 75 ret =
diff --git a/src/datastore/plugin_datastore_postgres.c b/src/datastore/plugin_datastore_postgres.c
index b6aeb0be6..1c9ded4f4 100644
--- a/src/datastore/plugin_datastore_postgres.c
+++ b/src/datastore/plugin_datastore_postgres.c
@@ -72,7 +72,8 @@ init_connection (struct Plugin *plugin)
72{ 72{
73 PGresult *ret; 73 PGresult *ret;
74 74
75 plugin->dbh = GNUNET_POSTGRES_connect (plugin->env->cfg, "datastore-postgres"); 75 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg,
76 "datastore-postgres");
76 if (NULL == plugin->dbh) 77 if (NULL == plugin->dbh)
77 return GNUNET_SYSERR; 78 return GNUNET_SYSERR;
78 79
diff --git a/src/include/gnunet_pq_lib.h b/src/include/gnunet_pq_lib.h
index 756370b74..5e54813e3 100644
--- a/src/include/gnunet_pq_lib.h
+++ b/src/include/gnunet_pq_lib.h
@@ -25,6 +25,9 @@
25#include "gnunet_util_lib.h" 25#include "gnunet_util_lib.h"
26 26
27 27
28/* ************************* pq_query_helper.c functions ************************ */
29
30
28/** 31/**
29 * Function called to convert input argument into SQL parameters. 32 * Function called to convert input argument into SQL parameters.
30 * 33 *
@@ -188,6 +191,9 @@ struct GNUNET_PQ_QueryParam
188GNUNET_PQ_query_param_uint64 (const uint64_t *x); 191GNUNET_PQ_query_param_uint64 (const uint64_t *x);
189 192
190 193
194/* ************************* pq_result_helper.c functions ************************ */
195
196
191/** 197/**
192 * Extract data from a Postgres database @a result at row @a row. 198 * Extract data from a Postgres database @a result at row @a row.
193 * 199 *
@@ -412,6 +418,8 @@ GNUNET_PQ_result_spec_uint64 (const char *name,
412 uint64_t *u64); 418 uint64_t *u64);
413 419
414 420
421/* ************************* pq.c functions ************************ */
422
415/** 423/**
416 * Execute a prepared statement. 424 * Execute a prepared statement.
417 * 425 *
@@ -419,6 +427,7 @@ GNUNET_PQ_result_spec_uint64 (const char *name,
419 * @param name name of the prepared statement 427 * @param name name of the prepared statement
420 * @param params parameters to the statement 428 * @param params parameters to the statement
421 * @return postgres result 429 * @return postgres result
430 * @deprecated (should become an internal API)
422 */ 431 */
423PGresult * 432PGresult *
424GNUNET_PQ_exec_prepared (PGconn *db_conn, 433GNUNET_PQ_exec_prepared (PGconn *db_conn,
@@ -435,6 +444,7 @@ GNUNET_PQ_exec_prepared (PGconn *db_conn,
435 * @return 444 * @return
436 * #GNUNET_YES if all results could be extracted 445 * #GNUNET_YES if all results could be extracted
437 * #GNUNET_SYSERR if a result was invalid (non-existing field) 446 * #GNUNET_SYSERR if a result was invalid (non-existing field)
447 * @deprecated (should become an internal API)
438 */ 448 */
439int 449int
440GNUNET_PQ_extract_result (PGresult *result, 450GNUNET_PQ_extract_result (PGresult *result,
@@ -452,6 +462,289 @@ void
452GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs); 462GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs);
453 463
454 464
465/* ******************** pq_eval.c functions ************** */
466
467
468/**
469 * Status code returned from functions running PQ commands.
470 * Can be combined with a function that returns the number
471 * of results, so non-negative values indicate success.
472 */
473enum GNUNET_PQ_QueryStatus
474{
475 /**
476 * A hard error occurred, retrying will not help.
477 */
478 GNUNET_PQ_STATUS_HARD_ERROR = -2,
479
480 /**
481 * A soft error occurred, retrying the transaction may succeed.
482 */
483 GNUNET_PQ_STATUS_SOFT_ERROR = -1,
484
485 /**
486 * The transaction succeeded, but yielded zero results.
487 */
488 GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS = 0,
489
490 /**
491 * The transaction succeeded, and yielded one result.
492 */
493 GNUNET_PQ_STATUS_SUCCESS_ONE_RESULT = 1
494
495};
496
497
498/**
499 * Check the @a result's error code to see what happened.
500 * Also logs errors.
501 *
502 * @param connection connection to execute the statement in
503 * @param statement_name name of the statement that created @a result
504 * @param result result to check
505 * @return status code from the result, mapping PQ status
506 * codes to `enum GNUNET_PQ_QueryStatus`. Never
507 * returns positive values as this function does
508 * not look at the result set.
509 * @deprecated (low level, let's see if we can do with just the high-level functions)
510 */
511enum GNUNET_PQ_QueryStatus
512GNUNET_PQ_eval_result (PGconn *connection,
513 const char *statement_name,
514 PGresult *result);
515
516
517/**
518 * Execute a named prepared @a statement that is NOT a SELECT
519 * statement in @a connnection using the given @a params. Returns the
520 * resulting session state.
521 *
522 * @param connection connection to execute the statement in
523 * @param statement_name name of the statement
524 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
525 * @return status code from the result, mapping PQ status
526 * codes to `enum GNUNET_PQ_QueryStatus`. Never
527 * returns positive values as this function does
528 * not look at the result set.
529 */
530enum GNUNET_PQ_QueryStatus
531GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
532 const char *statement_name,
533 const struct GNUNET_PQ_QueryParam *params);
534
535
536/**
537 * Function to be called with the results of a SELECT statement
538 * that has returned @a num_results results.
539 *
540 * @param cls closure
541 * @param result the postgres result
542 * @param num_result the number of results in @a result
543 */
544typedef void
545(*GNUNET_PQ_PostgresResultHandler)(void *cls,
546 PGresult *result,
547 unsigned int num_results);
548
549
550/**
551 * Execute a named prepared @a statement that is a SELECT statement
552 * which may return multiple results in @a connection using the given
553 * @a params. Call @a rh with the results. Returns the query
554 * status including the number of results given to @a rh (possibly zero).
555 * @a rh will not have been called if the return value is negative.
556 *
557 * @param connection connection to execute the statement in
558 * @param statement_name name of the statement
559 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
560 * @param rh function to call with the result set, NULL to ignore
561 * @param rh_cls closure to pass to @a rh
562 * @return status code from the result, mapping PQ status
563 * codes to `enum GNUNET_PQ_QueryStatus`.
564 */
565enum GNUNET_PQ_QueryStatus
566GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
567 const char *statement_name,
568 const struct GNUNET_PQ_QueryParam *params,
569 GNUNET_PQ_PostgresResultHandler rh,
570 void *rh_cls);
571
572
573/**
574 * Execute a named prepared @a statement that is a SELECT statement
575 * which must return a single result in @a connection using the given
576 * @a params. Stores the result (if any) in @a rs, which the caller
577 * must then clean up using #GNUNET_PQ_cleanup_result() if the return
578 * value was #GNUNET_PQ_STATUS_SUCCESS_ONE_RESULT. Returns the
579 * resulting session status.
580 *
581 * @param connection connection to execute the statement in
582 * @param statement_name name of the statement
583 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
584 * @param[in,out] rs result specification to use for storing the result of the query
585 * @return status code from the result, mapping PQ status
586 * codes to `enum GNUNET_PQ_QueryStatus`.
587 */
588enum GNUNET_PQ_QueryStatus
589GNUNET_PQ_eval_prepared_singleton_select (PGconn *connection,
590 const char *statement_name,
591 const struct GNUNET_PQ_QueryParam *params,
592 struct GNUNET_PQ_ResultSpec *rs);
593
594
595/* ******************** pq_prepare.c functions ************** */
596
597
598/**
599 * Information needed to prepare a list of SQL statements using
600 * #GNUNET_PQ_prepare_statements().
601 */
602struct GNUNET_PQ_PreparedStatement {
603
604 /**
605 * Name of the statement.
606 */
607 const char *name;
608
609 /**
610 * Actual SQL statement.
611 */
612 const char *sql;
613
614 /**
615 * Number of arguments included in @e sql.
616 */
617 unsigned int num_arguments;
618
619};
620
621
622/**
623 * Terminator for prepared statement list.
624 */
625#define GNUNET_PQ_PREPARED_STATEMENT_END { NULL, NULL, 0 }
626
627
628/**
629 * Create a `struct GNUNET_PQ_PreparedStatement`.
630 *
631 * @param name name of the statement
632 * @param sql actual SQL statement
633 * @param num_args number of arguments in the statement
634 * @return initialized struct
635 */
636struct GNUNET_PQ_PreparedStatement
637GNUNET_PQ_make_prepare (const char *name,
638 const char *sql,
639 unsigned int num_args);
640
641
642/**
643 * Request creation of prepared statements @a ps from Postgres.
644 *
645 * @param connection connection to prepare the statements for
646 * @param ps #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
647 * statements.
648 * @return #GNUNET_OK on success,
649 * #GNUNET_SYSERR on error
650 */
651int
652GNUNET_PQ_prepare_statements (PGconn *connection,
653 const struct GNUNET_PQ_PreparedStatement *ps);
654
655
656/* ******************** pq_exec.c functions ************** */
657
658
659/**
660 * Information needed to run a list of SQL statements using
661 * #GNUNET_PQ_exec_statements().
662 */
663struct GNUNET_PQ_ExecuteStatement {
664
665 /**
666 * Actual SQL statement.
667 */
668 const char *sql;
669
670 /**
671 * Should we ignore errors?
672 */
673 int ignore_errors;
674
675};
676
677
678/**
679 * Terminator for executable statement list.
680 */
681#define GNUNET_PQ_EXECUTE_STATEMENT_END { NULL, GNUNET_SYSERR }
682
683
684/**
685 * Create a `struct GNUNET_PQ_ExecuteStatement` where errors are fatal.
686 *
687 * @param sql actual SQL statement
688 * @return initialized struct
689 */
690struct GNUNET_PQ_ExecuteStatement
691GNUNET_PQ_make_execute (const char *sql);
692
693
694/**
695 * Create a `struct GNUNET_PQ_ExecuteStatement` where errors should
696 * be tolerated.
697 *
698 * @param sql actual SQL statement
699 * @return initialized struct
700 */
701struct GNUNET_PQ_ExecuteStatement
702GNUNET_PQ_make_try_execute (const char *sql);
703
704
705/**
706 * Request execution of an array of statements @a es from Postgres.
707 *
708 * @param connection connection to execute the statements over
709 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
710 * statements.
711 * @return #GNUNET_OK on success (modulo statements where errors can be ignored)
712 * #GNUNET_SYSERR on error
713 */
714int
715GNUNET_PQ_exec_statements (PGconn *connection,
716 const struct GNUNET_PQ_ExecuteStatement *es);
717
718
719/* ******************** pq_connect.c functions ************** */
720
721
722/**
723 * Create a connection to the Postgres database using @a config_str
724 * for the configuration. Initialize logging via GNUnet's log
725 * routines and disable Postgres's logger.
726 *
727 * @param config_str configuration to use
728 * @return NULL on error
729 */
730PGconn *
731GNUNET_PQ_connect (const char *config_str);
732
733
734/**
735 * Connect to a postgres database using the configuration
736 * option "CONFIG" in @a section.
737 *
738 * @param cfg configuration
739 * @param section configuration section to use to get Postgres configuration options
740 * @return the postgres handle, NULL on error
741 */
742PGconn *
743GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
744 const char *section);
745
746
747
455#endif /* GNUNET_PQ_LIB_H_ */ 748#endif /* GNUNET_PQ_LIB_H_ */
456 749
457/* end of include/gnunet_pq_lib.h */ 750/* end of include/gnunet_pq_lib.h */
diff --git a/src/namecache/plugin_namecache_postgres.c b/src/namecache/plugin_namecache_postgres.c
index bec8bffd2..9c85f4470 100644
--- a/src/namecache/plugin_namecache_postgres.c
+++ b/src/namecache/plugin_namecache_postgres.c
@@ -1,6 +1,6 @@
1 /* 1 /*
2 * This file is part of GNUnet 2 * This file is part of GNUnet
3 * Copyright (C) 2009-2013, 2016 GNUnet e.V. 3 * Copyright (C) 2009-2013, 2016, 2017 GNUnet e.V.
4 * 4 *
5 * GNUnet is free software; you can redistribute it and/or modify 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 6 * it under the terms of the GNU General Public License as published
@@ -72,40 +72,34 @@ struct Plugin
72 72
73 73
74/** 74/**
75 * Create our database indices.
76 *
77 * @param dbh handle to the database
78 */
79static void
80create_indices (PGconn * dbh)
81{
82 /* create indices */
83 if ( (GNUNET_OK !=
84 GNUNET_POSTGRES_exec (dbh,
85 "CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)")) ||
86 (GNUNET_OK !=
87 GNUNET_POSTGRES_exec (dbh,
88 "CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)")) )
89 LOG (GNUNET_ERROR_TYPE_ERROR,
90 _("Failed to create indices\n"));
91}
92
93
94/**
95 * Initialize the database connections and associated 75 * Initialize the database connections and associated
96 * data structures (create tables and indices 76 * data structures (create tables and indices
97 * as needed as well). 77 * as needed as well).
98 * 78 *
99 * @param plugin the plugin context (state for this module) 79 * @param plugin the plugin context (state for this module)
100 * @return GNUNET_OK on success 80 * @return #GNUNET_OK on success
101 */ 81 */
102static int 82static int
103database_setup (struct Plugin *plugin) 83database_setup (struct Plugin *plugin)
104{ 84{
105 PGresult *res; 85 struct GNUNET_PQ_ExecuteStatement es_temporary =
106 86 GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS ns096blocks ("
107 plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg, 87 " query BYTEA NOT NULL DEFAULT '',"
108 "namecache-postgres"); 88 " block BYTEA NOT NULL DEFAULT '',"
89 " expiration_time BIGINT NOT NULL DEFAULT 0"
90 ")"
91 "WITH OIDS");
92 struct GNUNET_PQ_ExecuteStatement es_default =
93 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks ("
94 " query BYTEA NOT NULL DEFAULT '',"
95 " block BYTEA NOT NULL DEFAULT '',"
96 " expiration_time BIGINT NOT NULL DEFAULT 0"
97 ")"
98 "WITH OIDS");
99 const struct GNUNET_PQ_ExecuteStatement *cr;
100
101 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
102 "namecache-postgres");
109 if (NULL == plugin->dbh) 103 if (NULL == plugin->dbh)
110 return GNUNET_SYSERR; 104 return GNUNET_SYSERR;
111 if (GNUNET_YES == 105 if (GNUNET_YES ==
@@ -113,65 +107,56 @@ database_setup (struct Plugin *plugin)
113 "namecache-postgres", 107 "namecache-postgres",
114 "TEMPORARY_TABLE")) 108 "TEMPORARY_TABLE"))
115 { 109 {
116 res = 110 cr = &es_temporary;
117 PQexec (plugin->dbh,
118 "CREATE TEMPORARY TABLE ns096blocks ("
119 " query BYTEA NOT NULL DEFAULT '',"
120 " block BYTEA NOT NULL DEFAULT '',"
121 " expiration_time BIGINT NOT NULL DEFAULT 0"
122 ")" "WITH OIDS");
123 } 111 }
124 else 112 else
125 { 113 {
126 res = 114 cr = &es_default;
127 PQexec (plugin->dbh,
128 "CREATE TABLE ns096blocks ("
129 " query BYTEA NOT NULL DEFAULT '',"
130 " block BYTEA NOT NULL DEFAULT '',"
131 " expiration_time BIGINT NOT NULL DEFAULT 0"
132 ")" "WITH OIDS");
133 } 115 }
134 if ( (NULL == res) || 116
135 ((PQresultStatus (res) != PGRES_COMMAND_OK) &&
136 (0 != strcmp ("42P07", /* duplicate table */
137 PQresultErrorField
138 (res,
139 PG_DIAG_SQLSTATE)))))
140 { 117 {
141 (void) GNUNET_POSTGRES_check_result (plugin->dbh, res, 118 struct GNUNET_PQ_ExecuteStatement es[] = {
142 PGRES_COMMAND_OK, "CREATE TABLE", 119 *cr,
143 "ns096blocks"); 120 GNUNET_PQ_make_try_execute ("CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)"),
144 PQfinish (plugin->dbh); 121 GNUNET_PQ_make_try_execute ("CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)"),
145 plugin->dbh = NULL; 122 GNUNET_PQ_EXECUTE_STATEMENT_END
146 return GNUNET_SYSERR; 123 };
124
125 if (GNUNET_OK !=
126 GNUNET_PQ_exec_statements (plugin->dbh,
127 es))
128 {
129 PQfinish (plugin->dbh);
130 plugin->dbh = NULL;
131 return GNUNET_SYSERR;
132 }
147 } 133 }
148 if (PQresultStatus (res) == PGRES_COMMAND_OK)
149 create_indices (plugin->dbh);
150 PQclear (res);
151 134
152 if ((GNUNET_OK !=
153 GNUNET_POSTGRES_prepare (plugin->dbh,
154 "cache_block",
155 "INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
156 "($1, $2, $3)", 3)) ||
157 (GNUNET_OK !=
158 GNUNET_POSTGRES_prepare (plugin->dbh,
159 "expire_blocks",
160 "DELETE FROM ns096blocks WHERE expiration_time<$1", 1)) ||
161 (GNUNET_OK !=
162 GNUNET_POSTGRES_prepare (plugin->dbh,
163 "delete_block",
164 "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2)) ||
165 (GNUNET_OK !=
166 GNUNET_POSTGRES_prepare (plugin->dbh,
167 "lookup_block",
168 "SELECT block FROM ns096blocks WHERE query=$1"
169 " ORDER BY expiration_time DESC LIMIT 1", 1)))
170 { 135 {
171 PQfinish (plugin->dbh); 136 struct GNUNET_PQ_PreparedStatement ps[] = {
172 plugin->dbh = NULL; 137 GNUNET_PQ_make_prepare ("cache_block",
173 return GNUNET_SYSERR; 138 "INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
139 "($1, $2, $3)", 3),
140 GNUNET_PQ_make_prepare ("expire_blocks",
141 "DELETE FROM ns096blocks WHERE expiration_time<$1", 1),
142 GNUNET_PQ_make_prepare ("delete_block",
143 "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2),
144 GNUNET_PQ_make_prepare ("lookup_block",
145 "SELECT block FROM ns096blocks WHERE query=$1"
146 " ORDER BY expiration_time DESC LIMIT 1", 1),
147 GNUNET_PQ_PREPARED_STATEMENT_END
148 };
149
150 if (GNUNET_OK !=
151 GNUNET_PQ_prepare_statements (plugin->dbh,
152 ps))
153 {
154 PQfinish (plugin->dbh);
155 plugin->dbh = NULL;
156 return GNUNET_SYSERR;
157 }
174 } 158 }
159
175 return GNUNET_OK; 160 return GNUNET_OK;
176} 161}
177 162
@@ -185,7 +170,7 @@ static void
185namecache_postgres_expire_blocks (struct Plugin *plugin) 170namecache_postgres_expire_blocks (struct Plugin *plugin)
186{ 171{
187 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); 172 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
188 struct GNUNET_PQ_QueryParam params[] = { 173 struct GNUNET_PQ_QueryParam params[] = {
189 GNUNET_PQ_query_param_absolute_time (&now), 174 GNUNET_PQ_query_param_absolute_time (&now),
190 GNUNET_PQ_query_param_end 175 GNUNET_PQ_query_param_end
191 }; 176 };
@@ -217,7 +202,7 @@ delete_old_block (struct Plugin *plugin,
217 const struct GNUNET_HashCode *query, 202 const struct GNUNET_HashCode *query,
218 struct GNUNET_TIME_AbsoluteNBO expiration_time) 203 struct GNUNET_TIME_AbsoluteNBO expiration_time)
219{ 204{
220 struct GNUNET_PQ_QueryParam params[] = { 205 struct GNUNET_PQ_QueryParam params[] = {
221 GNUNET_PQ_query_param_auto_from_type (query), 206 GNUNET_PQ_query_param_auto_from_type (query),
222 GNUNET_PQ_query_param_absolute_time_nbo (&expiration_time), 207 GNUNET_PQ_query_param_absolute_time_nbo (&expiration_time),
223 GNUNET_PQ_query_param_end 208 GNUNET_PQ_query_param_end
@@ -254,7 +239,7 @@ namecache_postgres_cache_block (void *cls,
254 size_t block_size = ntohl (block->purpose.size) + 239 size_t block_size = ntohl (block->purpose.size) +
255 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) + 240 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
256 sizeof (struct GNUNET_CRYPTO_EcdsaSignature); 241 sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
257 struct GNUNET_PQ_QueryParam params[] = { 242 struct GNUNET_PQ_QueryParam params[] = {
258 GNUNET_PQ_query_param_auto_from_type (&query), 243 GNUNET_PQ_query_param_auto_from_type (&query),
259 GNUNET_PQ_query_param_fixed_size (block, block_size), 244 GNUNET_PQ_query_param_fixed_size (block, block_size),
260 GNUNET_PQ_query_param_absolute_time_nbo (&block->expiration_time), 245 GNUNET_PQ_query_param_absolute_time_nbo (&block->expiration_time),
@@ -271,7 +256,9 @@ namecache_postgres_cache_block (void *cls,
271 GNUNET_break (0); 256 GNUNET_break (0);
272 return GNUNET_SYSERR; 257 return GNUNET_SYSERR;
273 } 258 }
274 delete_old_block (plugin, &query, block->expiration_time); 259 delete_old_block (plugin,
260 &query,
261 block->expiration_time);
275 262
276 res = GNUNET_PQ_exec_prepared (plugin->dbh, 263 res = GNUNET_PQ_exec_prepared (plugin->dbh,
277 "cache_block", 264 "cache_block",
@@ -301,10 +288,11 @@ namecache_postgres_cache_block (void *cls,
301static int 288static int
302namecache_postgres_lookup_block (void *cls, 289namecache_postgres_lookup_block (void *cls,
303 const struct GNUNET_HashCode *query, 290 const struct GNUNET_HashCode *query,
304 GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls) 291 GNUNET_NAMECACHE_BlockCallback iter,
292 void *iter_cls)
305{ 293{
306 struct Plugin *plugin = cls; 294 struct Plugin *plugin = cls;
307 struct GNUNET_PQ_QueryParam params[] = { 295 struct GNUNET_PQ_QueryParam params[] = {
308 GNUNET_PQ_query_param_auto_from_type (query), 296 GNUNET_PQ_query_param_auto_from_type (query),
309 GNUNET_PQ_query_param_end 297 GNUNET_PQ_query_param_end
310 }; 298 };
diff --git a/src/namestore/plugin_namestore_postgres.c b/src/namestore/plugin_namestore_postgres.c
index 01dbf9e61..4bf931c93 100644
--- a/src/namestore/plugin_namestore_postgres.c
+++ b/src/namestore/plugin_namestore_postgres.c
@@ -1,6 +1,6 @@
1 /* 1 /*
2 * This file is part of GNUnet 2 * This file is part of GNUnet
3 * Copyright (C) 2009-2013, 2016 GNUnet e.V. 3 * Copyright (C) 2009-2013, 2016, 2017 GNUnet e.V.
4 * 4 *
5 * GNUnet is free software; you can redistribute it and/or modify 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 6 * it under the terms of the GNU General Public License as published
@@ -75,30 +75,6 @@ struct Plugin
75 75
76 76
77/** 77/**
78 * Create our database indices.
79 *
80 * @param dbh handle to the database
81 */
82static void
83create_indices (PGconn * dbh)
84{
85 /* create indices */
86 if ( (GNUNET_OK !=
87 GNUNET_POSTGRES_exec (dbh,
88 "CREATE INDEX IF NOT EXISTS ir_pkey_reverse ON ns097records (zone_private_key,pkey)")) ||
89 (GNUNET_OK !=
90 GNUNET_POSTGRES_exec (dbh,
91 "CREATE INDEX IF NOT EXISTS ir_pkey_iter ON ns097records (zone_private_key,rvalue)")) ||
92 (GNUNET_OK !=
93 GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS it_iter ON ns097records (rvalue)")) ||
94 (GNUNET_OK !=
95 GNUNET_POSTGRES_exec (dbh, "CREATE INDEX IF NOT EXISTS ir_label ON ns097records (label)")) )
96 LOG (GNUNET_ERROR_TYPE_ERROR,
97 _("Failed to create indices\n"));
98}
99
100
101/**
102 * Initialize the database connections and associated 78 * Initialize the database connections and associated
103 * data structures (create tables and indices 79 * data structures (create tables and indices
104 * as needed as well). 80 * as needed as well).
@@ -109,10 +85,30 @@ create_indices (PGconn * dbh)
109static int 85static int
110database_setup (struct Plugin *plugin) 86database_setup (struct Plugin *plugin)
111{ 87{
112 PGresult *res; 88 struct GNUNET_PQ_ExecuteStatement es_temporary =
113 89 GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS ns097records ("
114 plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg, 90 " zone_private_key BYTEA NOT NULL DEFAULT '',"
115 "namestore-postgres"); 91 " pkey BYTEA DEFAULT '',"
92 " rvalue BYTEA NOT NULL DEFAULT '',"
93 " record_count INTEGER NOT NULL DEFAULT 0,"
94 " record_data BYTEA NOT NULL DEFAULT '',"
95 " label TEXT NOT NULL DEFAULT ''"
96 ")"
97 "WITH OIDS");
98 struct GNUNET_PQ_ExecuteStatement es_default =
99 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS ns097records ("
100 " zone_private_key BYTEA NOT NULL DEFAULT '',"
101 " pkey BYTEA DEFAULT '',"
102 " rvalue BYTEA NOT NULL DEFAULT '',"
103 " record_count INTEGER NOT NULL DEFAULT 0,"
104 " record_data BYTEA NOT NULL DEFAULT '',"
105 " label TEXT NOT NULL DEFAULT ''"
106 ")"
107 "WITH OIDS");
108 const struct GNUNET_PQ_ExecuteStatement *cr;
109
110 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
111 "namestore-postgres");
116 if (NULL == plugin->dbh) 112 if (NULL == plugin->dbh)
117 return GNUNET_SYSERR; 113 return GNUNET_SYSERR;
118 if (GNUNET_YES == 114 if (GNUNET_YES ==
@@ -120,80 +116,70 @@ database_setup (struct Plugin *plugin)
120 "namestore-postgres", 116 "namestore-postgres",
121 "TEMPORARY_TABLE")) 117 "TEMPORARY_TABLE"))
122 { 118 {
123 res = 119 cr = &es_temporary;
124 PQexec (plugin->dbh,
125 "CREATE TEMPORARY TABLE IF NOT EXISTS ns097records ("
126 " zone_private_key BYTEA NOT NULL DEFAULT '',"
127 " pkey BYTEA DEFAULT '',"
128 " rvalue BYTEA NOT NULL DEFAULT '',"
129 " record_count INTEGER NOT NULL DEFAULT 0,"
130 " record_data BYTEA NOT NULL DEFAULT '',"
131 " label TEXT NOT NULL DEFAULT ''"
132 ")" "WITH OIDS");
133 } 120 }
134 else 121 else
135 { 122 {
136 res = 123 cr = &es_default;
137 PQexec (plugin->dbh,
138 "CREATE TABLE IF NOT EXISTS ns097records ("
139 " zone_private_key BYTEA NOT NULL DEFAULT '',"
140 " pkey BYTEA DEFAULT '',"
141 " rvalue BYTEA NOT NULL DEFAULT '',"
142 " record_count INTEGER NOT NULL DEFAULT 0,"
143 " record_data BYTEA NOT NULL DEFAULT '',"
144 " label TEXT NOT NULL DEFAULT ''"
145 ")" "WITH OIDS");
146 } 124 }
147 if ( (NULL == res) || 125
148 ((PQresultStatus (res) != PGRES_COMMAND_OK) &&
149 (0 != strcmp ("42P07", /* duplicate table */
150 PQresultErrorField
151 (res,
152 PG_DIAG_SQLSTATE)))))
153 { 126 {
154 (void) GNUNET_POSTGRES_check_result (plugin->dbh, res, 127 struct GNUNET_PQ_ExecuteStatement es[] = {
155 PGRES_COMMAND_OK, "CREATE TABLE", 128 *cr,
156 "ns097records"); 129 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS ir_pkey_reverse "
157 PQfinish (plugin->dbh); 130 "ON ns097records (zone_private_key,pkey)"),
158 plugin->dbh = NULL; 131 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS ir_pkey_iter "
159 return GNUNET_SYSERR; 132 "ON ns097records (zone_private_key,rvalue)"),
133 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS it_iter "
134 "ON ns097records (rvalue)"),
135 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS ir_label "
136 "ON ns097records (label)"),
137 GNUNET_PQ_EXECUTE_STATEMENT_END
138 };
139
140 if (GNUNET_OK !=
141 GNUNET_PQ_exec_statements (plugin->dbh,
142 es))
143 {
144 PQfinish (plugin->dbh);
145 plugin->dbh = NULL;
146 return GNUNET_SYSERR;
147 }
160 } 148 }
161 create_indices (plugin->dbh); 149
162
163 if ((GNUNET_OK !=
164 GNUNET_POSTGRES_prepare (plugin->dbh,
165 "store_records",
166 "INSERT INTO ns097records (zone_private_key, pkey, rvalue, record_count, record_data, label) VALUES "
167 "($1, $2, $3, $4, $5, $6)", 6)) ||
168 (GNUNET_OK !=
169 GNUNET_POSTGRES_prepare (plugin->dbh,
170 "delete_records",
171 "DELETE FROM ns097records WHERE zone_private_key=$1 AND label=$2", 2)) ||
172 (GNUNET_OK !=
173 GNUNET_POSTGRES_prepare (plugin->dbh,
174 "zone_to_name",
175 "SELECT record_count,record_data,label FROM ns097records"
176 " WHERE zone_private_key=$1 AND pkey=$2", 2)) ||
177 (GNUNET_OK !=
178 GNUNET_POSTGRES_prepare (plugin->dbh,
179 "iterate_zone",
180 "SELECT record_count,record_data,label FROM ns097records"
181 " WHERE zone_private_key=$1 ORDER BY rvalue LIMIT 1 OFFSET $2", 2)) ||
182 (GNUNET_OK !=
183 GNUNET_POSTGRES_prepare (plugin->dbh,
184 "iterate_all_zones",
185 "SELECT record_count,record_data,label,zone_private_key"
186 " FROM ns097records ORDER BY rvalue LIMIT 1 OFFSET $1", 1)) ||
187 (GNUNET_OK !=
188 GNUNET_POSTGRES_prepare (plugin->dbh,
189 "lookup_label",
190 "SELECT record_count,record_data,label"
191 " FROM ns097records WHERE zone_private_key=$1 AND label=$2", 2)))
192 { 150 {
193 PQfinish (plugin->dbh); 151 struct GNUNET_PQ_PreparedStatement ps[] = {
194 plugin->dbh = NULL; 152 GNUNET_PQ_make_prepare ("store_records",
195 return GNUNET_SYSERR; 153 "INSERT INTO ns097records (zone_private_key, pkey, rvalue, record_count, record_data, label) VALUES "
154 "($1, $2, $3, $4, $5, $6)", 6),
155 GNUNET_PQ_make_prepare ("delete_records",
156 "DELETE FROM ns097records "
157 "WHERE zone_private_key=$1 AND label=$2", 2),
158 GNUNET_PQ_make_prepare ("zone_to_name",
159 "SELECT record_count,record_data,label FROM ns097records"
160 " WHERE zone_private_key=$1 AND pkey=$2", 2),
161 GNUNET_PQ_make_prepare ("iterate_zone",
162 "SELECT record_count,record_data,label FROM ns097records "
163 "WHERE zone_private_key=$1 ORDER BY rvalue LIMIT 1 OFFSET $2", 2),
164 GNUNET_PQ_make_prepare ("iterate_all_zones",
165 "SELECT record_count,record_data,label,zone_private_key"
166 " FROM ns097records ORDER BY rvalue LIMIT 1 OFFSET $1", 1),
167 GNUNET_PQ_make_prepare ("lookup_label",
168 "SELECT record_count,record_data,label "
169 "FROM ns097records WHERE zone_private_key=$1 AND label=$2", 2),
170 GNUNET_PQ_PREPARED_STATEMENT_END
171 };
172
173 if (GNUNET_OK !=
174 GNUNET_PQ_prepare_statements (plugin->dbh,
175 ps))
176 {
177 PQfinish (plugin->dbh);
178 plugin->dbh = NULL;
179 return GNUNET_SYSERR;
180 }
196 } 181 }
182
197 return GNUNET_OK; 183 return GNUNET_OK;
198} 184}
199 185
@@ -221,19 +207,19 @@ namestore_postgres_store_records (void *cls,
221 uint64_t rvalue; 207 uint64_t rvalue;
222 uint32_t rd_count_nbo = htonl ((uint32_t) rd_count); 208 uint32_t rd_count_nbo = htonl ((uint32_t) rd_count);
223 size_t data_size; 209 size_t data_size;
224 unsigned int i;
225 210
226 memset (&pkey, 0, sizeof (pkey)); 211 memset (&pkey, 0, sizeof (pkey));
227 for (i=0;i<rd_count;i++) 212 for (unsigned int i=0;i<rd_count;i++)
228 if (GNUNET_GNSRECORD_TYPE_PKEY == rd[i].record_type) 213 if (GNUNET_GNSRECORD_TYPE_PKEY == rd[i].record_type)
229 { 214 {
230 GNUNET_break (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) == rd[i].data_size); 215 GNUNET_break (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) == rd[i].data_size);
231 GNUNET_memcpy (&pkey, 216 GNUNET_memcpy (&pkey,
232 rd[i].data, 217 rd[i].data,
233 rd[i].data_size); 218 rd[i].data_size);
234 break; 219 break;
235 } 220 }
236 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); 221 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
222 UINT64_MAX);
237 data_size = GNUNET_GNSRECORD_records_get_size (rd_count, rd); 223 data_size = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
238 if (data_size > 64 * 65536) 224 if (data_size > 64 * 65536)
239 { 225 {
@@ -262,9 +248,10 @@ namestore_postgres_store_records (void *cls,
262 const int paramFormats[] = { 1, 1, 1, 1, 1, 1 }; 248 const int paramFormats[] = { 1, 1, 1, 1, 1, 1 };
263 PGresult *res; 249 PGresult *res;
264 250
265 if (data_size != GNUNET_GNSRECORD_records_serialize (rd_count, rd, 251 if (data_size !=
266 data_size, data)) 252 GNUNET_GNSRECORD_records_serialize (rd_count, rd,
267 { 253 data_size, data))
254 {
268 GNUNET_break (0); 255 GNUNET_break (0);
269 return GNUNET_SYSERR; 256 return GNUNET_SYSERR;
270 } 257 }
@@ -301,7 +288,8 @@ static int
301get_record_and_call_iterator (struct Plugin *plugin, 288get_record_and_call_iterator (struct Plugin *plugin,
302 PGresult *res, 289 PGresult *res,
303 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key, 290 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
304 GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls) 291 GNUNET_NAMESTORE_RecordIterator iter,
292 void *iter_cls)
305{ 293{
306 const char *data; 294 const char *data;
307 size_t data_size; 295 size_t data_size;
@@ -311,7 +299,9 @@ get_record_and_call_iterator (struct Plugin *plugin,
311 unsigned int cnt; 299 unsigned int cnt;
312 300
313 if (GNUNET_OK != 301 if (GNUNET_OK !=
314 GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK, 302 GNUNET_POSTGRES_check_result (plugin->dbh,
303 res,
304 PGRES_TUPLES_OK,
315 "PQexecPrepared", 305 "PQexecPrepared",
316 "iteration")) 306 "iteration"))
317 { 307 {
diff --git a/src/postgres/postgres.c b/src/postgres/postgres.c
index 14095c5a4..828842d9d 100644
--- a/src/postgres/postgres.c
+++ b/src/postgres/postgres.c
@@ -161,48 +161,6 @@ GNUNET_POSTGRES_prepare_ (PGconn *dbh,
161 161
162 162
163/** 163/**
164 * Connect to a postgres database
165 *
166 * @param cfg configuration
167 * @param section configuration section to use to get Postgres configuration options
168 * @return the postgres handle
169 */
170PGconn *
171GNUNET_POSTGRES_connect (const struct GNUNET_CONFIGURATION_Handle * cfg,
172 const char *section)
173{
174 PGconn *dbh;
175 char *conninfo;
176
177 /* Open database and precompile statements */
178 if (GNUNET_OK !=
179 GNUNET_CONFIGURATION_get_value_string (cfg,
180 section,
181 "CONFIG",
182 &conninfo))
183 conninfo = NULL;
184 dbh = PQconnectdb (conninfo == NULL ? "" : conninfo);
185
186 if (NULL != dbh)
187 {
188 if (PQstatus (dbh) != CONNECTION_OK)
189 {
190 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
191 "postgres",
192 _("Unable to connect to Postgres database '%s': %s\n"),
193 conninfo,
194 PQerrorMessage (dbh));
195 PQfinish (dbh);
196 dbh = NULL;
197 }
198 }
199 // FIXME: warn about out-of-memory when dbh is NULL?
200 GNUNET_free_non_null (conninfo);
201 return dbh;
202}
203
204
205/**
206 * Delete the row identified by the given rowid (qid 164 * Delete the row identified by the given rowid (qid
207 * in postgres). 165 * in postgres).
208 * 166 *
diff --git a/src/pq/Makefile.am b/src/pq/Makefile.am
index 8bb0a0132..d0c71703b 100644
--- a/src/pq/Makefile.am
+++ b/src/pq/Makefile.am
@@ -15,6 +15,10 @@ endif
15 15
16libgnunetpq_la_SOURCES = \ 16libgnunetpq_la_SOURCES = \
17 pq.c \ 17 pq.c \
18 pq_connect.c \
19 pq_eval.c \
20 pq_exec.c \
21 pq_prepare.c \
18 pq_query_helper.c \ 22 pq_query_helper.c \
19 pq_result_helper.c 23 pq_result_helper.c
20libgnunetpq_la_LIBADD = -lpq \ 24libgnunetpq_la_LIBADD = -lpq \
diff --git a/src/pq/pq_connect.c b/src/pq/pq_connect.c
new file mode 100644
index 000000000..99ad06485
--- /dev/null
+++ b/src/pq/pq_connect.c
@@ -0,0 +1,127 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017 GNUnet e.V.
4
5 GNUnet is free software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the Free Software
7 Foundation; either version 3, or (at your option) any later version.
8
9 GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along with
14 GNUnet; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
15*/
16/**
17 * @file pq/pq_connect.c
18 * @brief functions to connect to libpq (PostGres)
19 * @author Christian Grothoff
20 */
21#include "platform.h"
22#include "gnunet_util_lib.h"
23#include "gnunet_pq_lib.h"
24
25
26/**
27 * Function called by libpq whenever it wants to log something.
28 * We already log whenever we care, so this function does nothing
29 * and merely exists to silence the libpq logging.
30 *
31 * @param arg the SQL connection that was used
32 * @param res information about some libpq event
33 */
34static void
35pq_notice_receiver_cb (void *arg,
36 const PGresult *res)
37{
38 /* do nothing, intentionally */
39}
40
41
42/**
43 * Function called by libpq whenever it wants to log something.
44 * We log those using the Taler logger.
45 *
46 * @param arg the SQL connection that was used
47 * @param message information about some libpq event
48 */
49static void
50pq_notice_processor_cb (void *arg,
51 const char *message)
52{
53 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
54 "pq",
55 "%s",
56 message);
57}
58
59
60/**
61 * Create a connection to the Postgres database using @a config_str
62 * for the configuration. Initialize logging via GNUnet's log
63 * routines and disable Postgres's logger.
64 *
65 * @param config_str configuration to use
66 * @return NULL on error
67 */
68PGconn *
69GNUNET_PQ_connect (const char *config_str)
70{
71 PGconn *conn;
72
73 conn = PQconnectdb (config_str);
74 if ( (NULL == conn) ||
75 (CONNECTION_OK !=
76 PQstatus (conn)) )
77 {
78 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
79 "pq",
80 "Database connection to '%s' failed: %s\n",
81 config_str,
82 (NULL != conn) ?
83 PQerrorMessage (conn)
84 : "PQconnectdb returned NULL");
85 if (NULL != conn)
86 PQfinish (conn);
87 return NULL;
88 }
89 PQsetNoticeReceiver (conn,
90 &pq_notice_receiver_cb,
91 conn);
92 PQsetNoticeProcessor (conn,
93 &pq_notice_processor_cb,
94 conn);
95 return conn;
96}
97
98
99/**
100 * Connect to a postgres database using the configuration
101 * option "CONFIG" in @a section.
102 *
103 * @param cfg configuration
104 * @param section configuration section to use to get Postgres configuration options
105 * @return the postgres handle, NULL on error
106 */
107PGconn *
108GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle * cfg,
109 const char *section)
110{
111 PGconn *dbh;
112 char *conninfo;
113
114 /* Open database and precompile statements */
115 if (GNUNET_OK !=
116 GNUNET_CONFIGURATION_get_value_string (cfg,
117 section,
118 "CONFIG",
119 &conninfo))
120 conninfo = NULL;
121 dbh = GNUNET_PQ_connect (conninfo == NULL ? "" : conninfo);
122 GNUNET_free_non_null (conninfo);
123 return dbh;
124}
125
126
127/* end of pq/pq_connect.c */
diff --git a/src/pq/pq_eval.c b/src/pq/pq_eval.c
new file mode 100644
index 000000000..39a7a2c98
--- /dev/null
+++ b/src/pq/pq_eval.c
@@ -0,0 +1,249 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017 GNUnet e.V.
4
5 GNUnet is free software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the Free Software
7 Foundation; either version 3, or (at your option) any later version.
8
9 GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along with
14 GNUnet; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
15*/
16/**
17 * @file pq/pq_eval.c
18 * @brief functions to execute SQL statements with arguments and/or results (PostGres)
19 * @author Christian Grothoff
20 */
21#include "platform.h"
22#include "gnunet_util_lib.h"
23#include "gnunet_pq_lib.h"
24
25
26/**
27 * Error code returned by Postgres for deadlock.
28 */
29#define PQ_DIAG_SQLSTATE_DEADLOCK "40P01"
30
31/**
32 * Error code returned by Postgres for uniqueness violation.
33 */
34#define PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION "23505"
35
36/**
37 * Error code returned by Postgres on serialization failure.
38 */
39#define PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE "40001"
40
41
42/**
43 * Check the @a result's error code to see what happened.
44 * Also logs errors.
45 *
46 * @param connection connection to execute the statement in
47 * @param statement_name name of the statement that created @a result
48 * @param result result to check
49 * @return status code from the result, mapping PQ status
50 * codes to `enum GNUNET_PQ_QueryStatus`. Never
51 * returns positive values as this function does
52 * not look at the result set.
53 * @deprecated (low level, let's see if we can do with just the high-level functions)
54 */
55enum GNUNET_PQ_QueryStatus
56GNUNET_PQ_eval_result (PGconn *connection,
57 const char *statement_name,
58 PGresult *result)
59{
60 if (PGRES_COMMAND_OK !=
61 PQresultStatus (result))
62 {
63 const char *sqlstate;
64
65 sqlstate = PQresultErrorField (result,
66 PG_DIAG_SQLSTATE);
67 if (NULL == sqlstate)
68 {
69 /* very unexpected... */
70 GNUNET_break (0);
71 return GNUNET_PQ_STATUS_HARD_ERROR;
72 }
73 if ( (0 == strcmp (sqlstate,
74 PQ_DIAG_SQLSTATE_DEADLOCK)) ||
75 (0 == strcmp (sqlstate,
76 PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE)) )
77 {
78 /* These two can be retried and have a fair chance of working
79 the next time */
80 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
81 "pq",
82 "Query `%s' failed with result: %s/%s/%s/%s/%s\n",
83 statement_name,
84 PQresultErrorField (result,
85 PG_DIAG_MESSAGE_PRIMARY),
86 PQresultErrorField (result,
87 PG_DIAG_MESSAGE_DETAIL),
88 PQresultErrorMessage (result),
89 PQresStatus (PQresultStatus (result)),
90 PQerrorMessage (connection));
91 return GNUNET_PQ_STATUS_SOFT_ERROR;
92 }
93 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
94 "pq",
95 "Query `%s' failed with result: %s/%s/%s/%s/%s\n",
96 statement_name,
97 PQresultErrorField (result,
98 PG_DIAG_MESSAGE_PRIMARY),
99 PQresultErrorField (result,
100 PG_DIAG_MESSAGE_DETAIL),
101 PQresultErrorMessage (result),
102 PQresStatus (PQresultStatus (result)),
103 PQerrorMessage (connection));
104 return GNUNET_PQ_STATUS_HARD_ERROR;
105 }
106 return GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS;
107}
108
109
110/**
111 * Execute a named prepared @a statement that is NOT a SELECT
112 * statement in @a connnection using the given @a params. Returns the
113 * resulting session state.
114 *
115 * @param connection connection to execute the statement in
116 * @param statement_name name of the statement
117 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
118 * @return status code from the result, mapping PQ status
119 * codes to `enum GNUNET_PQ_QueryStatus`. Never
120 * returns positive values as this function does
121 * not look at the result set.
122 */
123enum GNUNET_PQ_QueryStatus
124GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
125 const char *statement_name,
126 const struct GNUNET_PQ_QueryParam *params)
127{
128 PGresult *result;
129 enum GNUNET_PQ_QueryStatus qs;
130
131 result = GNUNET_PQ_exec_prepared (connection,
132 statement_name,
133 params);
134 qs = GNUNET_PQ_eval_result (connection,
135 statement_name,
136 result);
137 PQclear (result);
138 return qs;
139}
140
141
142/**
143 * Execute a named prepared @a statement that is a SELECT statement
144 * which may return multiple results in @a connection using the given
145 * @a params. Call @a rh with the results. Returns the query
146 * status including the number of results given to @a rh (possibly zero).
147 * @a rh will not have been called if the return value is negative.
148 *
149 * @param connection connection to execute the statement in
150 * @param statement_name name of the statement
151 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
152 * @param rh function to call with the result set, NULL to ignore
153 * @param rh_cls closure to pass to @a rh
154 * @return status code from the result, mapping PQ status
155 * codes to `enum GNUNET_PQ_QueryStatus`.
156 */
157enum GNUNET_PQ_QueryStatus
158GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
159 const char *statement_name,
160 const struct GNUNET_PQ_QueryParam *params,
161 GNUNET_PQ_PostgresResultHandler rh,
162 void *rh_cls)
163{
164 PGresult *result;
165 enum GNUNET_PQ_QueryStatus qs;
166 unsigned int ret;
167
168 result = GNUNET_PQ_exec_prepared (connection,
169 statement_name,
170 params);
171 qs = GNUNET_PQ_eval_result (connection,
172 statement_name,
173 result);
174 if (qs < 0)
175 {
176 PQclear (result);
177 return qs;
178 }
179 ret = PQntuples (result);
180 if (NULL != rh)
181 rh (rh_cls,
182 result,
183 ret);
184 PQclear (result);
185 return ret;
186}
187
188
189/**
190 * Execute a named prepared @a statement that is a SELECT statement
191 * which must return a single result in @a connection using the given
192 * @a params. Stores the result (if any) in @a rs, which the caller
193 * must then clean up using #GNUNET_PQ_cleanup_result() if the return
194 * value was #GNUNET_PQ_STATUS_SUCCESS_ONE_RESULT. Returns the
195 * resulting session status.
196 *
197 * @param connection connection to execute the statement in
198 * @param statement_name name of the statement
199 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
200 * @param[in,out] rs result specification to use for storing the result of the query
201 * @return status code from the result, mapping PQ status
202 * codes to `enum GNUNET_PQ_QueryStatus`.
203 */
204enum GNUNET_PQ_QueryStatus
205GNUNET_PQ_eval_prepared_singleton_select (PGconn *connection,
206 const char *statement_name,
207 const struct GNUNET_PQ_QueryParam *params,
208 struct GNUNET_PQ_ResultSpec *rs)
209{
210 PGresult *result;
211 enum GNUNET_PQ_QueryStatus qs;
212
213 result = GNUNET_PQ_exec_prepared (connection,
214 statement_name,
215 params);
216 qs = GNUNET_PQ_eval_result (connection,
217 statement_name,
218 result);
219 if (qs < 0)
220 {
221 PQclear (result);
222 return qs;
223 }
224 if (0 == PQntuples (result))
225 {
226 PQclear (result);
227 return GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS;
228 }
229 if (1 != PQntuples (result))
230 {
231 /* more than one result, but there must be at most one */
232 GNUNET_break (0);
233 PQclear (result);
234 return GNUNET_PQ_STATUS_HARD_ERROR;
235 }
236 if (GNUNET_OK !=
237 GNUNET_PQ_extract_result (result,
238 rs,
239 0))
240 {
241 PQclear (result);
242 return GNUNET_PQ_STATUS_HARD_ERROR;
243 }
244 PQclear (result);
245 return GNUNET_PQ_STATUS_SUCCESS_ONE_RESULT;
246}
247
248
249/* end of pq/pq_eval.c */
diff --git a/src/pq/pq_exec.c b/src/pq/pq_exec.c
new file mode 100644
index 000000000..1e5e4eb76
--- /dev/null
+++ b/src/pq/pq_exec.c
@@ -0,0 +1,106 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017 GNUnet e.V.
4
5 GNUnet is free software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the Free Software
7 Foundation; either version 3, or (at your option) any later version.
8
9 GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along with
14 GNUnet; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
15*/
16/**
17 * @file pq/pq_exec.c
18 * @brief functions to execute plain SQL statements (PostGres)
19 * @author Christian Grothoff
20 */
21#include "platform.h"
22#include "gnunet_util_lib.h"
23#include "gnunet_pq_lib.h"
24
25
26/**
27 * Create a `struct GNUNET_PQ_ExecuteStatement` where errors are fatal.
28 *
29 * @param sql actual SQL statement
30 * @return initialized struct
31 */
32struct GNUNET_PQ_ExecuteStatement
33GNUNET_PQ_make_execute (const char *sql)
34{
35 struct GNUNET_PQ_ExecuteStatement es = {
36 .sql = sql,
37 .ignore_errors = GNUNET_NO
38 };
39
40 return es;
41}
42
43
44/**
45 * Create a `struct GNUNET_PQ_ExecuteStatement` where errors should
46 * be tolerated.
47 *
48 * @param sql actual SQL statement
49 * @return initialized struct
50 */
51struct GNUNET_PQ_ExecuteStatement
52GNUNET_PQ_make_try_execute (const char *sql)
53{
54 struct GNUNET_PQ_ExecuteStatement es = {
55 .sql = sql,
56 .ignore_errors = GNUNET_YES
57 };
58
59 return es;
60}
61
62
63/**
64 * Request execution of an array of statements @a es from Postgres.
65 *
66 * @param connection connection to execute the statements over
67 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
68 * statements.
69 * @return #GNUNET_OK on success (modulo statements where errors can be ignored)
70 * #GNUNET_SYSERR on error
71 */
72int
73GNUNET_PQ_exec_statements (PGconn *connection,
74 const struct GNUNET_PQ_ExecuteStatement *es)
75{
76 for (unsigned int i=0; NULL != es[i].sql; i++)
77 {
78 PGresult *result;
79
80 result = PQexec (connection,
81 es[i].sql);
82
83 if ( (GNUNET_NO == es[i].ignore_errors) &&
84 (PGRES_COMMAND_OK != PQresultStatus (result)) )
85 {
86 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
87 "pq",
88 "Failed to execute `%s': %s/%s/%s/%s/%s",
89 es[i].sql,
90 PQresultErrorField (result,
91 PG_DIAG_MESSAGE_PRIMARY),
92 PQresultErrorField (result,
93 PG_DIAG_MESSAGE_DETAIL),
94 PQresultErrorMessage (result),
95 PQresStatus (PQresultStatus (result)),
96 PQerrorMessage (connection));
97 PQclear (result);
98 return GNUNET_SYSERR;
99 }
100 PQclear (result);
101 }
102 return GNUNET_OK;
103}
104
105
106/* end of pq/pq_exec.c */
diff --git a/src/pq/pq_prepare.c b/src/pq/pq_prepare.c
new file mode 100644
index 000000000..f533cb564
--- /dev/null
+++ b/src/pq/pq_prepare.c
@@ -0,0 +1,92 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017 GNUnet e.V.
4
5 GNUnet is free software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the Free Software
7 Foundation; either version 3, or (at your option) any later version.
8
9 GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along with
14 GNUnet; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
15*/
16/**
17 * @file pq/pq_prepare.c
18 * @brief functions to connect to libpq (PostGres)
19 * @author Christian Grothoff
20 */
21#include "platform.h"
22#include "gnunet_util_lib.h"
23#include "gnunet_pq_lib.h"
24
25
26/**
27 * Create a `struct GNUNET_PQ_PreparedStatement`.
28 *
29 * @param name name of the statement
30 * @param sql actual SQL statement
31 * @param num_args number of arguments in the statement
32 * @return initialized struct
33 */
34struct GNUNET_PQ_PreparedStatement
35GNUNET_PQ_make_prepare (const char *name,
36 const char *sql,
37 unsigned int num_args)
38{
39 struct GNUNET_PQ_PreparedStatement ps = {
40 .name = name,
41 .sql = sql,
42 .num_arguments = num_args
43 };
44
45 return ps;
46}
47
48
49/**
50 * Request creation of prepared statements @a ps from Postgres.
51 *
52 * @param connection connection to prepare the statements for
53 * @param ps #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
54 * statements.
55 * @return #GNUNET_OK on success,
56 * #GNUNET_SYSERR on error
57 */
58int
59GNUNET_PQ_prepare_statements (PGconn *connection,
60 const struct GNUNET_PQ_PreparedStatement *ps)
61{
62 for (unsigned int i=0;NULL != ps[i].name;i++)
63 {
64 PGresult *ret;
65
66 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
67 "pq",
68 "Preparing SQL statement `%s' as `%s'\n",
69 ps[i].sql,
70 ps[i].name);
71 ret = PQprepare (connection,
72 ps[i].name,
73 ps[i].sql,
74 ps[i].num_arguments,
75 NULL);
76 if (PGRES_COMMAND_OK != PQresultStatus (ret))
77 {
78 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
79 "pq",
80 _("PQprepare (`%s' as `%s') failed with error: %s\n"),
81 ps[i].sql,
82 ps[i].name,
83 PQerrorMessage (connection));
84 PQclear (ret);
85 return GNUNET_SYSERR;
86 }
87 }
88 return GNUNET_OK;
89}
90
91
92/* end of pq/pq_prepare.c */
diff --git a/src/psycstore/plugin_psycstore_postgres.c b/src/psycstore/plugin_psycstore_postgres.c
index 273ab4e80..f410e2737 100644
--- a/src/psycstore/plugin_psycstore_postgres.c
+++ b/src/psycstore/plugin_psycstore_postgres.c
@@ -84,117 +84,96 @@ struct Plugin
84 * as needed as well). 84 * as needed as well).
85 * 85 *
86 * @param plugin the plugin context (state for this module) 86 * @param plugin the plugin context (state for this module)
87 * @return GNUNET_OK on success 87 * @return #GNUNET_OK on success
88 */ 88 */
89static int 89static int
90database_setup (struct Plugin *plugin) 90database_setup (struct Plugin *plugin)
91{ 91{
92 struct GNUNET_PQ_ExecuteStatement es[] = {
93 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS channels (\n"
94 " id SERIAL,\n"
95 " pub_key BYTEA NOT NULL CHECK (LENGTH(pub_key)=32),\n"
96 " max_state_message_id BIGINT,\n"
97 " state_hash_message_id BIGINT,\n"
98 " PRIMARY KEY(id)\n"
99 ")"
100 "WITH OIDS"),
101 GNUNET_PQ_make_execute ("CREATE UNIQUE INDEX IF NOT EXISTS channel_pub_key_idx \n"
102 " ON channels (pub_key)"),
103 GNUNET_PQ_make_execute ("CREATE OR REPLACE FUNCTION get_chan_id(BYTEA) RETURNS INTEGER AS \n"
104 " 'SELECT id FROM channels WHERE pub_key=$1;' LANGUAGE SQL STABLE \n"
105 "RETURNS NULL ON NULL INPUT"),
106 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS slaves (\n"
107 " id SERIAL,\n"
108 " pub_key BYTEA NOT NULL CHECK (LENGTH(pub_key)=32),\n"
109 " PRIMARY KEY(id)\n"
110 ")"
111 "WITH OIDS"),
112 GNUNET_PQ_make_execute ("CREATE UNIQUE INDEX IF NOT EXISTS slaves_pub_key_idx \n"
113 " ON slaves (pub_key)"),
114 GNUNET_PQ_make_execute ("CREATE OR REPLACE FUNCTION get_slave_id(BYTEA) RETURNS INTEGER AS \n"
115 " 'SELECT id FROM slaves WHERE pub_key=$1;' LANGUAGE SQL STABLE \n"
116 "RETURNS NULL ON NULL INPUT"),
117 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS membership (\n"
118 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
119 " slave_id BIGINT NOT NULL REFERENCES slaves(id),\n"
120 " did_join INT NOT NULL,\n"
121 " announced_at BIGINT NOT NULL,\n"
122 " effective_since BIGINT NOT NULL,\n"
123 " group_generation BIGINT NOT NULL\n"
124 ")"
125 "WITH OIDS"),
126 GNUNET_PQ_make_execute ("CREATE INDEX IF NOT EXISTS idx_membership_channel_id_slave_id "
127 "ON membership (channel_id, slave_id)"),
128 /** @todo messages table: add method_name column */
129 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS messages (\n"
130 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
131 " hop_counter INT NOT NULL,\n"
132 " signature BYTEA CHECK (LENGTH(signature)=64),\n"
133 " purpose BYTEA CHECK (LENGTH(purpose)=8),\n"
134 " fragment_id BIGINT NOT NULL,\n"
135 " fragment_offset BIGINT NOT NULL,\n"
136 " message_id BIGINT NOT NULL,\n"
137 " group_generation BIGINT NOT NULL,\n"
138 " multicast_flags INT NOT NULL,\n"
139 " psycstore_flags INT NOT NULL,\n"
140 " data BYTEA,\n"
141 " PRIMARY KEY (channel_id, fragment_id),\n"
142 " UNIQUE (channel_id, message_id, fragment_offset)\n"
143 ")"
144 "WITH OIDS"),
145 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS state (\n"
146 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
147 " name TEXT NOT NULL,\n"
148 " value_current BYTEA,\n"
149 " value_signed BYTEA,\n"
150 " PRIMARY KEY (channel_id, name)\n"
151 ")"
152 "WITH OIDS"),
153 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS state_sync (\n"
154 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
155 " name TEXT NOT NULL,\n"
156 " value BYTEA,\n"
157 " PRIMARY KEY (channel_id, name)\n"
158 ")"
159 "WITH OIDS"),
160 GNUNET_PQ_EXECUTE_STATEMENT_END
161 };
162
92 /* Open database and precompile statements */ 163 /* Open database and precompile statements */
93 plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg, 164 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
94 "psycstore-postgres"); 165 "psycstore-postgres");
95 if (NULL == plugin->dbh) 166 if (NULL == plugin->dbh)
96 return GNUNET_SYSERR; 167 return GNUNET_SYSERR;
97 168 if (GNUNET_OK !=
98 /* Create tables */ 169 GNUNET_PQ_exec_statements (plugin->dbh,
99 if ((GNUNET_OK != 170 es))
100 GNUNET_POSTGRES_exec(plugin->dbh,
101 "CREATE TABLE IF NOT EXISTS channels (\n"
102 " id SERIAL,\n"
103 " pub_key BYTEA NOT NULL CHECK (LENGTH(pub_key)=32),\n"
104 " max_state_message_id BIGINT,\n"
105 " state_hash_message_id BIGINT,\n"
106 " PRIMARY KEY(id)\n"
107 ")" "WITH OIDS")) ||
108
109 (GNUNET_OK !=
110 GNUNET_POSTGRES_exec(plugin->dbh,
111 "CREATE UNIQUE INDEX IF NOT EXISTS channel_pub_key_idx \n"
112 " ON channels (pub_key)")) ||
113
114 (GNUNET_OK !=
115 GNUNET_POSTGRES_exec(plugin->dbh,
116 "CREATE OR REPLACE FUNCTION get_chan_id(BYTEA) RETURNS INTEGER AS \n"
117 " 'SELECT id FROM channels WHERE pub_key=$1;' LANGUAGE SQL STABLE \n"
118 "RETURNS NULL ON NULL INPUT")) ||
119
120 (GNUNET_OK !=
121 GNUNET_POSTGRES_exec(plugin->dbh,
122 "CREATE TABLE IF NOT EXISTS slaves (\n"
123 " id SERIAL,\n"
124 " pub_key BYTEA NOT NULL CHECK (LENGTH(pub_key)=32),\n"
125 " PRIMARY KEY(id)\n"
126 ")" "WITH OIDS")) ||
127
128 (GNUNET_OK !=
129 GNUNET_POSTGRES_exec(plugin->dbh,
130 "CREATE UNIQUE INDEX IF NOT EXISTS slaves_pub_key_idx \n"
131 " ON slaves (pub_key)")) ||
132
133 (GNUNET_OK !=
134 GNUNET_POSTGRES_exec(plugin->dbh,
135 "CREATE OR REPLACE FUNCTION get_slave_id(BYTEA) RETURNS INTEGER AS \n"
136 " 'SELECT id FROM slaves WHERE pub_key=$1;' LANGUAGE SQL STABLE \n"
137 "RETURNS NULL ON NULL INPUT")) ||
138
139 (GNUNET_OK !=
140 GNUNET_POSTGRES_exec(plugin->dbh,
141 "CREATE TABLE IF NOT EXISTS membership (\n"
142 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
143 " slave_id BIGINT NOT NULL REFERENCES slaves(id),\n"
144 " did_join INT NOT NULL,\n"
145 " announced_at BIGINT NOT NULL,\n"
146 " effective_since BIGINT NOT NULL,\n"
147 " group_generation BIGINT NOT NULL\n"
148 ")" "WITH OIDS")) ||
149
150 (GNUNET_OK !=
151 GNUNET_POSTGRES_exec(plugin->dbh,
152 "CREATE INDEX IF NOT EXISTS idx_membership_channel_id_slave_id "
153 "ON membership (channel_id, slave_id)")) ||
154
155 /** @todo messages table: add method_name column */
156 (GNUNET_OK !=
157 GNUNET_POSTGRES_exec(plugin->dbh,
158 "CREATE TABLE IF NOT EXISTS messages (\n"
159 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
160 " hop_counter INT NOT NULL,\n"
161 " signature BYTEA CHECK (LENGTH(signature)=64),\n"
162 " purpose BYTEA CHECK (LENGTH(purpose)=8),\n"
163 " fragment_id BIGINT NOT NULL,\n"
164 " fragment_offset BIGINT NOT NULL,\n"
165 " message_id BIGINT NOT NULL,\n"
166 " group_generation BIGINT NOT NULL,\n"
167 " multicast_flags INT NOT NULL,\n"
168 " psycstore_flags INT NOT NULL,\n"
169 " data BYTEA,\n"
170 " PRIMARY KEY (channel_id, fragment_id),\n"
171 " UNIQUE (channel_id, message_id, fragment_offset)\n"
172 ")" "WITH OIDS")) ||
173
174 (GNUNET_OK !=
175 GNUNET_POSTGRES_exec(plugin->dbh,
176 "CREATE TABLE IF NOT EXISTS state (\n"
177 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
178 " name TEXT NOT NULL,\n"
179 " value_current BYTEA,\n"
180 " value_signed BYTEA,\n"
181 " PRIMARY KEY (channel_id, name)\n"
182 ")" "WITH OIDS")) ||
183 (GNUNET_OK !=
184 GNUNET_POSTGRES_exec(plugin->dbh,
185 "CREATE TABLE IF NOT EXISTS state_sync (\n"
186 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
187 " name TEXT NOT NULL,\n"
188 " value BYTEA,\n"
189 " PRIMARY KEY (channel_id, name)\n"
190 ")" "WITH OIDS")))
191 { 171 {
192 PQfinish (plugin->dbh); 172 PQfinish (plugin->dbh);
193 plugin->dbh = NULL; 173 plugin->dbh = NULL;
194 return GNUNET_SYSERR; 174 return GNUNET_SYSERR;
195 } 175 }
196 176
197
198 /* Prepare statements */ 177 /* Prepare statements */
199 if ((GNUNET_OK != GNUNET_POSTGRES_prepare (plugin->dbh, 178 if ((GNUNET_OK != GNUNET_POSTGRES_prepare (plugin->dbh,
200 "transaction_begin", 179 "transaction_begin",
@@ -842,7 +821,6 @@ fragment_row (struct Plugin *plugin,
842 void *purpose = NULL; 821 void *purpose = NULL;
843 size_t signature_size; 822 size_t signature_size;
844 size_t purpose_size; 823 size_t purpose_size;
845
846 uint64_t fragment_id; 824 uint64_t fragment_id;
847 uint64_t fragment_offset; 825 uint64_t fragment_offset;
848 uint64_t message_id; 826 uint64_t message_id;
@@ -852,9 +830,7 @@ fragment_row (struct Plugin *plugin,
852 size_t buf_size; 830 size_t buf_size;
853 int ret = GNUNET_SYSERR; 831 int ret = GNUNET_SYSERR;
854 struct GNUNET_MULTICAST_MessageHeader *mp; 832 struct GNUNET_MULTICAST_MessageHeader *mp;
855
856 uint32_t msg_flags; 833 uint32_t msg_flags;
857
858 struct GNUNET_PQ_ResultSpec results[] = { 834 struct GNUNET_PQ_ResultSpec results[] = {
859 GNUNET_PQ_result_spec_uint32 ("hop_counter", &hop_counter), 835 GNUNET_PQ_result_spec_uint32 ("hop_counter", &hop_counter),
860 GNUNET_PQ_result_spec_variable_size ("signature", &signature, &signature_size), 836 GNUNET_PQ_result_spec_variable_size ("signature", &signature, &signature_size),
@@ -964,8 +940,6 @@ fragment_get (void *cls,
964 void *cb_cls) 940 void *cb_cls)
965{ 941{
966 struct Plugin *plugin = cls; 942 struct Plugin *plugin = cls;
967 *returned_fragments = 0;
968
969 struct GNUNET_PQ_QueryParam params_select[] = { 943 struct GNUNET_PQ_QueryParam params_select[] = {
970 GNUNET_PQ_query_param_auto_from_type (channel_key), 944 GNUNET_PQ_query_param_auto_from_type (channel_key),
971 GNUNET_PQ_query_param_uint64 (&first_fragment_id), 945 GNUNET_PQ_query_param_uint64 (&first_fragment_id),
@@ -973,7 +947,12 @@ fragment_get (void *cls,
973 GNUNET_PQ_query_param_end 947 GNUNET_PQ_query_param_end
974 }; 948 };
975 949
976 return fragment_select (plugin, "select_fragments", params_select, returned_fragments, cb, cb_cls); 950 *returned_fragments = 0;
951 return fragment_select (plugin,
952 "select_fragments",
953 params_select,
954 returned_fragments,
955 cb, cb_cls);
977} 956}
978 957
979 958
@@ -1002,7 +981,11 @@ fragment_get_latest (void *cls,
1002 GNUNET_PQ_query_param_end 981 GNUNET_PQ_query_param_end
1003 }; 982 };
1004 983
1005 return fragment_select (plugin, "select_latest_fragments", params_select, returned_fragments, cb, cb_cls); 984 return fragment_select (plugin,
985 "select_latest_fragments",
986 params_select,
987 returned_fragments,
988 cb, cb_cls);
1006} 989}
1007 990
1008 991
@@ -1024,11 +1007,6 @@ message_get (void *cls,
1024 void *cb_cls) 1007 void *cb_cls)
1025{ 1008{
1026 struct Plugin *plugin = cls; 1009 struct Plugin *plugin = cls;
1027 *returned_fragments = 0;
1028
1029 if (0 == fragment_limit)
1030 fragment_limit = INT64_MAX;
1031
1032 struct GNUNET_PQ_QueryParam params_select[] = { 1010 struct GNUNET_PQ_QueryParam params_select[] = {
1033 GNUNET_PQ_query_param_auto_from_type (channel_key), 1011 GNUNET_PQ_query_param_auto_from_type (channel_key),
1034 GNUNET_PQ_query_param_uint64 (&first_message_id), 1012 GNUNET_PQ_query_param_uint64 (&first_message_id),
@@ -1037,7 +1015,14 @@ message_get (void *cls,
1037 GNUNET_PQ_query_param_end 1015 GNUNET_PQ_query_param_end
1038 }; 1016 };
1039 1017
1040 return fragment_select (plugin, "select_messages", params_select, returned_fragments, cb, cb_cls); 1018 if (0 == fragment_limit)
1019 fragment_limit = INT64_MAX;
1020 *returned_fragments = 0;
1021 return fragment_select (plugin,
1022 "select_messages",
1023 params_select,
1024 returned_fragments,
1025 cb, cb_cls);
1041} 1026}
1042 1027
1043 1028
@@ -1057,8 +1042,6 @@ message_get_latest (void *cls,
1057 void *cb_cls) 1042 void *cb_cls)
1058{ 1043{
1059 struct Plugin *plugin = cls; 1044 struct Plugin *plugin = cls;
1060 *returned_fragments = 0;
1061
1062 struct GNUNET_PQ_QueryParam params_select[] = { 1045 struct GNUNET_PQ_QueryParam params_select[] = {
1063 GNUNET_PQ_query_param_auto_from_type (channel_key), 1046 GNUNET_PQ_query_param_auto_from_type (channel_key),
1064 GNUNET_PQ_query_param_auto_from_type (channel_key), 1047 GNUNET_PQ_query_param_auto_from_type (channel_key),
@@ -1066,7 +1049,12 @@ message_get_latest (void *cls,
1066 GNUNET_PQ_query_param_end 1049 GNUNET_PQ_query_param_end
1067 }; 1050 };
1068 1051
1069 return fragment_select (plugin, "select_latest_messages", params_select, returned_fragments, cb, cb_cls); 1052 *returned_fragments = 0;
1053 return fragment_select (plugin,
1054 "select_latest_messages",
1055 params_select,
1056 returned_fragments,
1057 cb, cb_cls);
1070} 1058}
1071 1059
1072 1060
@@ -1255,7 +1243,8 @@ state_assign (struct Plugin *plugin, const char *stmt,
1255 1243
1256 1244
1257static int 1245static int
1258update_message_id (struct Plugin *plugin, const char *stmt, 1246update_message_id (struct Plugin *plugin,
1247 const char *stmt,
1259 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key, 1248 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1260 uint64_t message_id) 1249 uint64_t message_id)
1261{ 1250{