summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/datacache/plugin_datacache_postgres.c25
-rw-r--r--src/datastore/plugin_datastore_postgres.c20
-rw-r--r--src/include/gnunet_pq_lib.h133
-rw-r--r--src/namecache/plugin_namecache_postgres.c38
-rw-r--r--src/namestore/plugin_namestore_postgres.c64
-rw-r--r--src/pq/Makefile.am2
-rw-r--r--src/pq/pq.c43
-rw-r--r--src/pq/pq.h57
-rw-r--r--src/pq/pq_connect.c169
-rw-r--r--src/pq/pq_eval.c59
-rw-r--r--src/pq/pq_exec.c15
-rw-r--r--src/pq/pq_prepare.c39
-rw-r--r--src/pq/pq_result_helper.c2
-rw-r--r--src/pq/test_pq.c186
14 files changed, 517 insertions, 335 deletions
diff --git a/src/datacache/plugin_datacache_postgres.c b/src/datacache/plugin_datacache_postgres.c
index 59dff9067..c532550ae 100644
--- a/src/datacache/plugin_datacache_postgres.c
+++ b/src/datacache/plugin_datacache_postgres.c
@@ -48,7 +48,7 @@ struct Plugin
48 /** 48 /**
49 * Native Postgres database handle. 49 * Native Postgres database handle.
50 */ 50 */
51 PGconn *dbh; 51 struct GNUNET_PQ_Context *dbh;
52 52
53 /** 53 /**
54 * Number of key-value pairs in the database. 54 * Number of key-value pairs in the database.
@@ -122,26 +122,11 @@ init_connection (struct Plugin *plugin)
122 }; 122 };
123 123
124 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg, 124 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg,
125 "datacache-postgres"); 125 "datacache-postgres",
126 es,
127 ps);
126 if (NULL == plugin->dbh) 128 if (NULL == plugin->dbh)
127 return GNUNET_SYSERR; 129 return GNUNET_SYSERR;
128 if (GNUNET_OK !=
129 GNUNET_PQ_exec_statements (plugin->dbh,
130 es))
131 {
132 PQfinish (plugin->dbh);
133 plugin->dbh = NULL;
134 return GNUNET_SYSERR;
135 }
136
137 if (GNUNET_OK !=
138 GNUNET_PQ_prepare_statements (plugin->dbh,
139 ps))
140 {
141 PQfinish (plugin->dbh);
142 plugin->dbh = NULL;
143 return GNUNET_SYSERR;
144 }
145 return GNUNET_OK; 130 return GNUNET_OK;
146} 131}
147 132
@@ -710,7 +695,7 @@ libgnunet_plugin_datacache_postgres_done (void *cls)
710 struct GNUNET_DATACACHE_PluginFunctions *api = cls; 695 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
711 struct Plugin *plugin = api->cls; 696 struct Plugin *plugin = api->cls;
712 697
713 PQfinish (plugin->dbh); 698 GNUNET_PQ_disconnect (plugin->dbh);
714 GNUNET_free (plugin); 699 GNUNET_free (plugin);
715 GNUNET_free (api); 700 GNUNET_free (api);
716 return NULL; 701 return NULL;
diff --git a/src/datastore/plugin_datastore_postgres.c b/src/datastore/plugin_datastore_postgres.c
index 181cf8cf8..0811edfd7 100644
--- a/src/datastore/plugin_datastore_postgres.c
+++ b/src/datastore/plugin_datastore_postgres.c
@@ -54,7 +54,7 @@ struct Plugin
54 /** 54 /**
55 * Native Postgres database handle. 55 * Native Postgres database handle.
56 */ 56 */
57 PGconn *dbh; 57 struct GNUNET_PQ_Context *dbh;
58}; 58};
59 59
60 60
@@ -172,21 +172,11 @@ init_connection (struct Plugin *plugin)
172#undef RESULT_COLUMNS 172#undef RESULT_COLUMNS
173 173
174 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg, 174 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->env->cfg,
175 "datastore-postgres"); 175 "datastore-postgres",
176 es,
177 ps);
176 if (NULL == plugin->dbh) 178 if (NULL == plugin->dbh)
177 return GNUNET_SYSERR; 179 return GNUNET_SYSERR;
178
179 if ((GNUNET_OK !=
180 GNUNET_PQ_exec_statements (plugin->dbh,
181 es)) ||
182 (GNUNET_OK !=
183 GNUNET_PQ_prepare_statements (plugin->dbh,
184 ps)))
185 {
186 PQfinish (plugin->dbh);
187 plugin->dbh = NULL;
188 return GNUNET_SYSERR;
189 }
190 return GNUNET_OK; 180 return GNUNET_OK;
191} 181}
192 182
@@ -974,7 +964,7 @@ libgnunet_plugin_datastore_postgres_done (void *cls)
974 struct GNUNET_DATASTORE_PluginFunctions *api = cls; 964 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
975 struct Plugin *plugin = api->cls; 965 struct Plugin *plugin = api->cls;
976 966
977 PQfinish (plugin->dbh); 967 GNUNET_PQ_disconnect (plugin->dbh);
978 GNUNET_free (plugin); 968 GNUNET_free (plugin);
979 GNUNET_free (api); 969 GNUNET_free (api);
980 return NULL; 970 return NULL;
diff --git a/src/include/gnunet_pq_lib.h b/src/include/gnunet_pq_lib.h
index 6c576c8ab..a56df21fd 100644
--- a/src/include/gnunet_pq_lib.h
+++ b/src/include/gnunet_pq_lib.h
@@ -46,15 +46,16 @@
46 * @param scratch_length number of entries left in @a scratch 46 * @param scratch_length number of entries left in @a scratch
47 * @return -1 on error, number of offsets used in @a scratch otherwise 47 * @return -1 on error, number of offsets used in @a scratch otherwise
48 */ 48 */
49typedef int (*GNUNET_PQ_QueryConverter) (void *cls, 49typedef int
50 const void *data, 50(*GNUNET_PQ_QueryConverter) (void *cls,
51 size_t data_len, 51 const void *data,
52 void *param_values[], 52 size_t data_len,
53 int param_lengths[], 53 void *param_values[],
54 int param_formats[], 54 int param_lengths[],
55 unsigned int param_length, 55 int param_formats[],
56 void *scratch[], 56 unsigned int param_length,
57 unsigned int scratch_length); 57 void *scratch[],
58 unsigned int scratch_length);
58 59
59 60
60/** 61/**
@@ -214,12 +215,13 @@ GNUNET_PQ_query_param_uint64 (const uint64_t *x);
214 * #GNUNET_YES if all results could be extracted 215 * #GNUNET_YES if all results could be extracted
215 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) 216 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
216 */ 217 */
217typedef int (*GNUNET_PQ_ResultConverter) (void *cls, 218typedef int
218 PGresult *result, 219(*GNUNET_PQ_ResultConverter) (void *cls,
219 int row, 220 PGresult *result,
220 const char *fname, 221 int row,
221 size_t *dst_size, 222 const char *fname,
222 void *dst); 223 size_t *dst_size,
224 void *dst);
223 225
224 226
225/** 227/**
@@ -229,7 +231,9 @@ typedef int (*GNUNET_PQ_ResultConverter) (void *cls,
229 * @param cls closure 231 * @param cls closure
230 * @param rd result data to clean up 232 * @param rd result data to clean up
231 */ 233 */
232typedef void (*GNUNET_PQ_ResultCleanup) (void *cls, void *rd); 234typedef void
235(*GNUNET_PQ_ResultCleanup) (void *cls,
236 void *rd);
233 237
234 238
235/** 239/**
@@ -420,16 +424,22 @@ GNUNET_PQ_result_spec_uint64 (const char *name, uint64_t *u64);
420/* ************************* pq.c functions ************************ */ 424/* ************************* pq.c functions ************************ */
421 425
422/** 426/**
427 * Postgres context.
428 */
429struct GNUNET_PQ_Context;
430
431
432/**
423 * Execute a prepared statement. 433 * Execute a prepared statement.
424 * 434 *
425 * @param db_conn database connection 435 * @param db database context
426 * @param name name of the prepared statement 436 * @param name name of the prepared statement
427 * @param params parameters to the statement 437 * @param params parameters to the statement
428 * @return postgres result 438 * @return postgres result
429 * @deprecated (should become an internal API) 439 * @deprecated (should become an internal API)
430 */ 440 */
431PGresult * 441PGresult *
432GNUNET_PQ_exec_prepared (PGconn *db_conn, 442GNUNET_PQ_exec_prepared (struct GNUNET_PQ_Context *db,
433 const char *name, 443 const char *name,
434 const struct GNUNET_PQ_QueryParam *params); 444 const struct GNUNET_PQ_QueryParam *params);
435 445
@@ -468,7 +478,7 @@ GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs);
468 * Check the @a result's error code to see what happened. 478 * Check the @a result's error code to see what happened.
469 * Also logs errors. 479 * Also logs errors.
470 * 480 *
471 * @param connection connection to execute the statement in 481 * @param db database to execute the statement in
472 * @param statement_name name of the statement that created @a result 482 * @param statement_name name of the statement that created @a result
473 * @param result result to check 483 * @param result result to check
474 * @return status code from the result, mapping PQ status 484 * @return status code from the result, mapping PQ status
@@ -478,7 +488,7 @@ GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs);
478 * @deprecated (low level, let's see if we can do with just the high-level functions) 488 * @deprecated (low level, let's see if we can do with just the high-level functions)
479 */ 489 */
480enum GNUNET_DB_QueryStatus 490enum GNUNET_DB_QueryStatus
481GNUNET_PQ_eval_result (PGconn *connection, 491GNUNET_PQ_eval_result (struct GNUNET_PQ_Context *db,
482 const char *statement_name, 492 const char *statement_name,
483 PGresult *result); 493 PGresult *result);
484 494
@@ -488,7 +498,7 @@ GNUNET_PQ_eval_result (PGconn *connection,
488 * statement in @a connnection using the given @a params. Returns the 498 * statement in @a connnection using the given @a params. Returns the
489 * resulting session state. 499 * resulting session state.
490 * 500 *
491 * @param connection connection to execute the statement in 501 * @param db database to execute the statement with
492 * @param statement_name name of the statement 502 * @param statement_name name of the statement
493 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated) 503 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
494 * @return status code from the result, mapping PQ status 504 * @return status code from the result, mapping PQ status
@@ -500,7 +510,7 @@ GNUNET_PQ_eval_result (PGconn *connection,
500 * zero; if INSERT was successful, we return one. 510 * zero; if INSERT was successful, we return one.
501 */ 511 */
502enum GNUNET_DB_QueryStatus 512enum GNUNET_DB_QueryStatus
503GNUNET_PQ_eval_prepared_non_select (PGconn *connection, 513GNUNET_PQ_eval_prepared_non_select (struct GNUNET_PQ_Context *db,
504 const char *statement_name, 514 const char *statement_name,
505 const struct GNUNET_PQ_QueryParam *params); 515 const struct GNUNET_PQ_QueryParam *params);
506 516
@@ -513,9 +523,10 @@ GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
513 * @param result the postgres result 523 * @param result the postgres result
514 * @param num_result the number of results in @a result 524 * @param num_result the number of results in @a result
515 */ 525 */
516typedef void (*GNUNET_PQ_PostgresResultHandler) (void *cls, 526typedef void
517 PGresult *result, 527(*GNUNET_PQ_PostgresResultHandler) (void *cls,
518 unsigned int num_results); 528 PGresult *result,
529 unsigned int num_results);
519 530
520 531
521/** 532/**
@@ -525,7 +536,7 @@ typedef void (*GNUNET_PQ_PostgresResultHandler) (void *cls,
525 * status including the number of results given to @a rh (possibly zero). 536 * status including the number of results given to @a rh (possibly zero).
526 * @a rh will not have been called if the return value is negative. 537 * @a rh will not have been called if the return value is negative.
527 * 538 *
528 * @param connection connection to execute the statement in 539 * @param db database to execute the statement with
529 * @param statement_name name of the statement 540 * @param statement_name name of the statement
530 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated) 541 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
531 * @param rh function to call with the result set, NULL to ignore 542 * @param rh function to call with the result set, NULL to ignore
@@ -534,7 +545,7 @@ typedef void (*GNUNET_PQ_PostgresResultHandler) (void *cls,
534 * codes to `enum GNUNET_DB_QueryStatus`. 545 * codes to `enum GNUNET_DB_QueryStatus`.
535 */ 546 */
536enum GNUNET_DB_QueryStatus 547enum GNUNET_DB_QueryStatus
537GNUNET_PQ_eval_prepared_multi_select (PGconn *connection, 548GNUNET_PQ_eval_prepared_multi_select (struct GNUNET_PQ_Context *db,
538 const char *statement_name, 549 const char *statement_name,
539 const struct GNUNET_PQ_QueryParam *params, 550 const struct GNUNET_PQ_QueryParam *params,
540 GNUNET_PQ_PostgresResultHandler rh, 551 GNUNET_PQ_PostgresResultHandler rh,
@@ -549,7 +560,7 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
549 * value was #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT. Returns the 560 * value was #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT. Returns the
550 * resulting session status. 561 * resulting session status.
551 * 562 *
552 * @param connection connection to execute the statement in 563 * @param db database to execute the statement with
553 * @param statement_name name of the statement 564 * @param statement_name name of the statement
554 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated) 565 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
555 * @param[in,out] rs result specification to use for storing the result of the query 566 * @param[in,out] rs result specification to use for storing the result of the query
@@ -557,11 +568,11 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
557 * codes to `enum GNUNET_DB_QueryStatus`. 568 * codes to `enum GNUNET_DB_QueryStatus`.
558 */ 569 */
559enum GNUNET_DB_QueryStatus 570enum GNUNET_DB_QueryStatus
560GNUNET_PQ_eval_prepared_singleton_select ( 571GNUNET_PQ_eval_prepared_singleton_select (struct GNUNET_PQ_Context *db,
561 PGconn *connection, 572 const char *statement_name,
562 const char *statement_name, 573 const struct
563 const struct GNUNET_PQ_QueryParam *params, 574 GNUNET_PQ_QueryParam *params,
564 struct GNUNET_PQ_ResultSpec *rs); 575 struct GNUNET_PQ_ResultSpec *rs);
565 576
566 577
567/* ******************** pq_prepare.c functions ************** */ 578/* ******************** pq_prepare.c functions ************** */
@@ -587,6 +598,7 @@ struct GNUNET_PQ_PreparedStatement
587 * Number of arguments included in @e sql. 598 * Number of arguments included in @e sql.
588 */ 599 */
589 unsigned int num_arguments; 600 unsigned int num_arguments;
601
590}; 602};
591 603
592 604
@@ -616,14 +628,14 @@ GNUNET_PQ_make_prepare (const char *name,
616/** 628/**
617 * Request creation of prepared statements @a ps from Postgres. 629 * Request creation of prepared statements @a ps from Postgres.
618 * 630 *
619 * @param connection connection to prepare the statements for 631 * @param db database to prepare the statements for
620 * @param ps #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared 632 * @param ps #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
621 * statements. 633 * statements.
622 * @return #GNUNET_OK on success, 634 * @return #GNUNET_OK on success,
623 * #GNUNET_SYSERR on error 635 * #GNUNET_SYSERR on error
624 */ 636 */
625int 637int
626GNUNET_PQ_prepare_statements (PGconn *connection, 638GNUNET_PQ_prepare_statements (struct GNUNET_PQ_Context *db,
627 const struct GNUNET_PQ_PreparedStatement *ps); 639 const struct GNUNET_PQ_PreparedStatement *ps);
628 640
629 641
@@ -681,14 +693,14 @@ GNUNET_PQ_make_try_execute (const char *sql);
681/** 693/**
682 * Request execution of an array of statements @a es from Postgres. 694 * Request execution of an array of statements @a es from Postgres.
683 * 695 *
684 * @param connection connection to execute the statements over 696 * @param pq database to execute the statements in
685 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared 697 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
686 * statements. 698 * statements.
687 * @return #GNUNET_OK on success (modulo statements where errors can be ignored) 699 * @return #GNUNET_OK on success (modulo statements where errors can be ignored)
688 * #GNUNET_SYSERR on error 700 * #GNUNET_SYSERR on error
689 */ 701 */
690int 702int
691GNUNET_PQ_exec_statements (PGconn *connection, 703GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db,
692 const struct GNUNET_PQ_ExecuteStatement *es); 704 const struct GNUNET_PQ_ExecuteStatement *es);
693 705
694 706
@@ -698,26 +710,61 @@ GNUNET_PQ_exec_statements (PGconn *connection,
698/** 710/**
699 * Create a connection to the Postgres database using @a config_str 711 * Create a connection to the Postgres database using @a config_str
700 * for the configuration. Initialize logging via GNUnet's log 712 * for the configuration. Initialize logging via GNUnet's log
701 * routines and disable Postgres's logger. 713 * routines and disable Postgres's logger. Also ensures that the
714 * statements in @a es are executed whenever we (re)connect to the
715 * database, and that the prepared statements in @a ps are "ready".
716 * If statements in @es fail that were created with
717 * #GNUNET_PQ_make_execute(), then the entire operation fails.
702 * 718 *
703 * @param config_str configuration to use 719 * @param config_str configuration to use
720 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated
721 * array of statements to execute upon EACH connection, can be NULL
722 * @param ps array of prepared statements to prepare, can be NULL
704 * @return NULL on error 723 * @return NULL on error
705 */ 724 */
706PGconn * 725struct GNUNET_PQ_Context *
707GNUNET_PQ_connect (const char *config_str); 726GNUNET_PQ_connect (const char *config_str,
727 const struct GNUNET_PQ_ExecuteStatement *es,
728 const struct GNUNET_PQ_PreparedStatement *ps);
708 729
709 730
710/** 731/**
711 * Connect to a postgres database using the configuration 732 * Connect to a postgres database using the configuration
712 * option "CONFIG" in @a section. 733 * option "CONFIG" in @a section. Also ensures that the
734 * statements in @a es are executed whenever we (re)connect to the
735 * database, and that the prepared statements in @a ps are "ready".
713 * 736 *
714 * @param cfg configuration 737 * @param cfg configuration
715 * @param section configuration section to use to get Postgres configuration options 738 * @param section configuration section to use to get Postgres configuration options
739 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated
740 * array of statements to execute upon EACH connection, can be NULL
741 * @param ps array of prepared statements to prepare, can be NULL
716 * @return the postgres handle, NULL on error 742 * @return the postgres handle, NULL on error
717 */ 743 */
718PGconn * 744struct GNUNET_PQ_Context *
719GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, 745GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
720 const char *section); 746 const char *section,
747 const struct GNUNET_PQ_ExecuteStatement *es,
748 const struct GNUNET_PQ_PreparedStatement *ps);
749
750
751/**
752 * Reinitialize the database @a db.
753 *
754 * @param db database connection to reinitialize
755 */
756void
757GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db);
758
759
760/**
761 * Disconnect from the database, destroying the prepared statements
762 * and releasing other associated resources.
763 *
764 * @param db database handle to disconnect (will be free'd)
765 */
766void
767GNUNET_PQ_disconnect (struct GNUNET_PQ_Context *db);
721 768
722 769
723#endif /* GNUNET_PQ_LIB_H_ */ 770#endif /* GNUNET_PQ_LIB_H_ */
diff --git a/src/namecache/plugin_namecache_postgres.c b/src/namecache/plugin_namecache_postgres.c
index e4b360ef2..35bf5c2ff 100644
--- a/src/namecache/plugin_namecache_postgres.c
+++ b/src/namecache/plugin_namecache_postgres.c
@@ -42,9 +42,9 @@ struct Plugin
42 const struct GNUNET_CONFIGURATION_Handle *cfg; 42 const struct GNUNET_CONFIGURATION_Handle *cfg;
43 43
44 /** 44 /**
45 * Native Postgres database handle. 45 * Postgres database handle.
46 */ 46 */
47 PGconn *dbh; 47 struct GNUNET_PQ_Context *dbh;
48}; 48};
49 49
50 50
@@ -75,10 +75,6 @@ database_setup (struct Plugin *plugin)
75 "WITH OIDS"); 75 "WITH OIDS");
76 const struct GNUNET_PQ_ExecuteStatement *cr; 76 const struct GNUNET_PQ_ExecuteStatement *cr;
77 77
78 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
79 "namecache-postgres");
80 if (NULL == plugin->dbh)
81 return GNUNET_SYSERR;
82 if (GNUNET_YES == 78 if (GNUNET_YES ==
83 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, 79 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
84 "namecache-postgres", 80 "namecache-postgres",
@@ -90,7 +86,6 @@ database_setup (struct Plugin *plugin)
90 { 86 {
91 cr = &es_default; 87 cr = &es_default;
92 } 88 }
93
94 { 89 {
95 struct GNUNET_PQ_ExecuteStatement es[] = { 90 struct GNUNET_PQ_ExecuteStatement es[] = {
96 *cr, 91 *cr,
@@ -100,18 +95,6 @@ database_setup (struct Plugin *plugin)
100 "CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)"), 95 "CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)"),
101 GNUNET_PQ_EXECUTE_STATEMENT_END 96 GNUNET_PQ_EXECUTE_STATEMENT_END
102 }; 97 };
103
104 if (GNUNET_OK !=
105 GNUNET_PQ_exec_statements (plugin->dbh,
106 es))
107 {
108 PQfinish (plugin->dbh);
109 plugin->dbh = NULL;
110 return GNUNET_SYSERR;
111 }
112 }
113
114 {
115 struct GNUNET_PQ_PreparedStatement ps[] = { 98 struct GNUNET_PQ_PreparedStatement ps[] = {
116 GNUNET_PQ_make_prepare ("cache_block", 99 GNUNET_PQ_make_prepare ("cache_block",
117 "INSERT INTO ns096blocks (query, block, expiration_time) VALUES " 100 "INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
@@ -128,16 +111,13 @@ database_setup (struct Plugin *plugin)
128 GNUNET_PQ_PREPARED_STATEMENT_END 111 GNUNET_PQ_PREPARED_STATEMENT_END
129 }; 112 };
130 113
131 if (GNUNET_OK != 114 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
132 GNUNET_PQ_prepare_statements (plugin->dbh, 115 "namecache-postgres",
133 ps)) 116 es,
134 { 117 ps);
135 PQfinish (plugin->dbh);
136 plugin->dbh = NULL;
137 return GNUNET_SYSERR;
138 }
139 } 118 }
140 119 if (NULL == plugin->dbh)
120 return GNUNET_SYSERR;
141 return GNUNET_OK; 121 return GNUNET_OK;
142} 122}
143 123
@@ -311,7 +291,7 @@ namecache_postgres_lookup_block (void *cls,
311static void 291static void
312database_shutdown (struct Plugin *plugin) 292database_shutdown (struct Plugin *plugin)
313{ 293{
314 PQfinish (plugin->dbh); 294 GNUNET_PQ_disconnect (plugin->dbh);
315 plugin->dbh = NULL; 295 plugin->dbh = NULL;
316} 296}
317 297
diff --git a/src/namestore/plugin_namestore_postgres.c b/src/namestore/plugin_namestore_postgres.c
index 5148ca0f5..23893538b 100644
--- a/src/namestore/plugin_namestore_postgres.c
+++ b/src/namestore/plugin_namestore_postgres.c
@@ -45,9 +45,9 @@ struct Plugin
45 const struct GNUNET_CONFIGURATION_Handle *cfg; 45 const struct GNUNET_CONFIGURATION_Handle *cfg;
46 46
47 /** 47 /**
48 * Native Postgres database handle. 48 * Postgres database handle.
49 */ 49 */
50 PGconn *dbh; 50 struct GNUNET_PQ_Context *dbh;
51}; 51};
52 52
53 53
@@ -88,30 +88,8 @@ database_setup (struct Plugin *plugin)
88 ")" 88 ")"
89 "WITH OIDS"); 89 "WITH OIDS");
90 const struct GNUNET_PQ_ExecuteStatement *cr; 90 const struct GNUNET_PQ_ExecuteStatement *cr;
91 struct GNUNET_PQ_ExecuteStatement sc = GNUNET_PQ_EXECUTE_STATEMENT_END;
91 92
92 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
93 "namestore-postgres");
94 if (NULL == plugin->dbh)
95 return GNUNET_SYSERR;
96 if (GNUNET_YES ==
97 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
98 "namestore-postgres",
99 "ASYNC_COMMIT"))
100 {
101 struct GNUNET_PQ_ExecuteStatement es[] = {
102 GNUNET_PQ_make_try_execute ("SET synchronous_commit TO off"),
103 GNUNET_PQ_EXECUTE_STATEMENT_END
104 };
105
106 if (GNUNET_OK !=
107 GNUNET_PQ_exec_statements (plugin->dbh,
108 es))
109 {
110 PQfinish (plugin->dbh);
111 plugin->dbh = NULL;
112 return GNUNET_SYSERR;
113 }
114 }
115 if (GNUNET_YES == 93 if (GNUNET_YES ==
116 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, 94 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
117 "namestore-postgres", 95 "namestore-postgres",
@@ -124,6 +102,12 @@ database_setup (struct Plugin *plugin)
124 cr = &es_default; 102 cr = &es_default;
125 } 103 }
126 104
105 if (GNUNET_YES ==
106 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
107 "namestore-postgres",
108 "ASYNC_COMMIT"))
109 sc = GNUNET_PQ_make_try_execute ("SET synchronous_commit TO off");
110
127 { 111 {
128 struct GNUNET_PQ_ExecuteStatement es[] = { 112 struct GNUNET_PQ_ExecuteStatement es[] = {
129 *cr, 113 *cr,
@@ -135,20 +119,9 @@ database_setup (struct Plugin *plugin)
135 "ON ns098records (label)"), 119 "ON ns098records (label)"),
136 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS zone_label " 120 GNUNET_PQ_make_try_execute ("CREATE INDEX IF NOT EXISTS zone_label "
137 "ON ns098records (zone_private_key,label)"), 121 "ON ns098records (zone_private_key,label)"),
122 sc,
138 GNUNET_PQ_EXECUTE_STATEMENT_END 123 GNUNET_PQ_EXECUTE_STATEMENT_END
139 }; 124 };
140
141 if (GNUNET_OK !=
142 GNUNET_PQ_exec_statements (plugin->dbh,
143 es))
144 {
145 PQfinish (plugin->dbh);
146 plugin->dbh = NULL;
147 return GNUNET_SYSERR;
148 }
149 }
150
151 {
152 struct GNUNET_PQ_PreparedStatement ps[] = { 125 struct GNUNET_PQ_PreparedStatement ps[] = {
153 GNUNET_PQ_make_prepare ("store_records", 126 GNUNET_PQ_make_prepare ("store_records",
154 "INSERT INTO ns098records" 127 "INSERT INTO ns098records"
@@ -183,16 +156,13 @@ database_setup (struct Plugin *plugin)
183 GNUNET_PQ_PREPARED_STATEMENT_END 156 GNUNET_PQ_PREPARED_STATEMENT_END
184 }; 157 };
185 158
186 if (GNUNET_OK != 159 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
187 GNUNET_PQ_prepare_statements (plugin->dbh, 160 "namestore-postgres",
188 ps)) 161 es,
189 { 162 ps);
190 PQfinish (plugin->dbh);
191 plugin->dbh = NULL;
192 return GNUNET_SYSERR;
193 }
194 } 163 }
195 164 if (NULL == plugin->dbh)
165 return GNUNET_SYSERR;
196 return GNUNET_OK; 166 return GNUNET_OK;
197} 167}
198 168
@@ -593,7 +563,7 @@ namestore_postgres_zone_to_name (void *cls,
593static void 563static void
594database_shutdown (struct Plugin *plugin) 564database_shutdown (struct Plugin *plugin)
595{ 565{
596 PQfinish (plugin->dbh); 566 GNUNET_PQ_disconnect (plugin->dbh);
597 plugin->dbh = NULL; 567 plugin->dbh = NULL;
598} 568}
599 569
diff --git a/src/pq/Makefile.am b/src/pq/Makefile.am
index 9270e6fe0..750a1d48d 100644
--- a/src/pq/Makefile.am
+++ b/src/pq/Makefile.am
@@ -22,7 +22,7 @@ libgnunetpq_la_LIBADD = -lpq \
22libgnunetpq_la_LDFLAGS = \ 22libgnunetpq_la_LDFLAGS = \
23 $(POSTGRESQL_LDFLAGS) \ 23 $(POSTGRESQL_LDFLAGS) \
24 $(GN_LIB_LDFLAGS) \ 24 $(GN_LIB_LDFLAGS) \
25 -version-info 0:0:0 25 -version-info 1:0:0
26 26
27if ENABLE_TEST_RUN 27if ENABLE_TEST_RUN
28TESTS = \ 28TESTS = \
diff --git a/src/pq/pq.c b/src/pq/pq.c
index 7e97c8f72..d2b9a6174 100644
--- a/src/pq/pq.c
+++ b/src/pq/pq.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet 2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2016 GNUnet e.V. 3 Copyright (C) 2014, 2015, 2016, 2017, 2019 GNUnet e.V.
4 4
5 GNUnet is free software: you can redistribute it and/or modify it 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 6 under the terms of the GNU Affero General Public License as published
@@ -25,33 +25,30 @@
25 * @author Christian Grothoff 25 * @author Christian Grothoff
26 */ 26 */
27#include "platform.h" 27#include "platform.h"
28#include "gnunet_util_lib.h" 28#include "pq.h"
29#include "gnunet_pq_lib.h"
30
31 29
32/** 30/**
33 * Execute a prepared statement. 31 * Execute a prepared statement.
34 * 32 *
35 * @param db_conn database connection 33 * @param db database handle
36 * @param name name of the prepared statement 34 * @param name name of the prepared statement
37 * @param params parameters to the statement 35 * @param params parameters to the statement
38 * @return postgres result 36 * @return postgres result
39 */ 37 */
40PGresult * 38PGresult *
41GNUNET_PQ_exec_prepared (PGconn *db_conn, 39GNUNET_PQ_exec_prepared (struct GNUNET_PQ_Context *db,
42 const char *name, 40 const char *name,
43 const struct GNUNET_PQ_QueryParam *params) 41 const struct GNUNET_PQ_QueryParam *params)
44{ 42{
45 unsigned int len; 43 unsigned int len;
46 unsigned int i;
47 44
48 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 45 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
49 "Running prepared statement `%s' on %p\n", 46 "Running prepared statement `%s' on %p\n",
50 name, 47 name,
51 db_conn); 48 db);
52 /* count the number of parameters */ 49 /* count the number of parameters */
53 len = 0; 50 len = 0;
54 for (i = 0; 0 != params[i].num_params; i++) 51 for (unsigned int i = 0; 0 != params[i].num_params; i++)
55 len += params[i].num_params; 52 len += params[i].num_params;
56 53
57 /* new scope to allow stack allocation without alloca */ 54 /* new scope to allow stack allocation without alloca */
@@ -67,10 +64,11 @@ GNUNET_PQ_exec_prepared (PGconn *db_conn,
67 unsigned int soff; 64 unsigned int soff;
68 PGresult *res; 65 PGresult *res;
69 int ret; 66 int ret;
67 ConnStatusType status;
70 68
71 off = 0; 69 off = 0;
72 soff = 0; 70 soff = 0;
73 for (i = 0; 0 != params[i].num_params; i++) 71 for (unsigned int i = 0; 0 != params[i].num_params; i++)
74 { 72 {
75 const struct GNUNET_PQ_QueryParam *x = &params[i]; 73 const struct GNUNET_PQ_QueryParam *x = &params[i];
76 74
@@ -97,13 +95,24 @@ GNUNET_PQ_exec_prepared (PGconn *db_conn,
97 "pq", 95 "pq",
98 "Executing prepared SQL statement `%s'\n", 96 "Executing prepared SQL statement `%s'\n",
99 name); 97 name);
100 res = PQexecPrepared (db_conn, 98 res = PQexecPrepared (db->conn,
101 name, 99 name,
102 len, 100 len,
103 (const char **) param_values, 101 (const char **) param_values,
104 param_lengths, 102 param_lengths,
105 param_formats, 103 param_formats,
106 1); 104 1);
105 if ( (PGRES_COMMAND_OK != PQresultStatus (res)) &&
106 (CONNECTION_OK != (status = PQstatus (db->conn))) )
107 {
108 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
109 "pq",
110 "Database disconnected on SQL statement `%s' (reconnecting)\n",
111 name);
112 GNUNET_PQ_reconnect (db);
113 res = NULL;
114 }
115
107 for (off = 0; off < soff; off++) 116 for (off = 0; off < soff; off++)
108 GNUNET_free (scratch[off]); 117 GNUNET_free (scratch[off]);
109 return res; 118 return res;
@@ -120,9 +129,7 @@ GNUNET_PQ_exec_prepared (PGconn *db_conn,
120void 129void
121GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs) 130GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs)
122{ 131{
123 unsigned int i; 132 for (unsigned int i = 0; NULL != rs[i].conv; i++)
124
125 for (i = 0; NULL != rs[i].conv; i++)
126 if (NULL != rs[i].cleaner) 133 if (NULL != rs[i].cleaner)
127 rs[i].cleaner (rs[i].cls, 134 rs[i].cleaner (rs[i].cls,
128 rs[i].dst); 135 rs[i].dst);
@@ -145,12 +152,12 @@ GNUNET_PQ_extract_result (PGresult *result,
145 struct GNUNET_PQ_ResultSpec *rs, 152 struct GNUNET_PQ_ResultSpec *rs,
146 int row) 153 int row)
147{ 154{
148 unsigned int i; 155 if (NULL == result)
149 int ret; 156 return GNUNET_SYSERR;
150 157 for (unsigned int i = 0; NULL != rs[i].conv; i++)
151 for (i = 0; NULL != rs[i].conv; i++)
152 { 158 {
153 struct GNUNET_PQ_ResultSpec *spec; 159 struct GNUNET_PQ_ResultSpec *spec;
160 int ret;
154 161
155 spec = &rs[i]; 162 spec = &rs[i];
156 ret = spec->conv (spec->cls, 163 ret = spec->conv (spec->cls,
diff --git a/src/pq/pq.h b/src/pq/pq.h
new file mode 100644
index 000000000..b30f4f0d4
--- /dev/null
+++ b/src/pq/pq.h
@@ -0,0 +1,57 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2017, 2019 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 * @file pq/pq.h
22 * @brief shared internal data structures of libgnunetpq
23 * @author Christian Grothoff
24 */
25#ifndef PQ_H
26#define PQ_H
27
28#include "gnunet_util_lib.h"
29#include "gnunet_pq_lib.h"
30
31/**
32 * Handle to Postgres database.
33 */
34struct GNUNET_PQ_Context
35{
36 /**
37 * Actual connection.
38 */
39 PGconn *conn;
40
41 /**
42 * Statements to execute upon connection.
43 */
44 struct GNUNET_PQ_ExecuteStatement *es;
45
46 /**
47 * Prepared statements.
48 */
49 struct GNUNET_PQ_PreparedStatement *ps;
50
51 /**
52 * Configuration to use to connect to the DB.
53 */
54 char *config_str;
55};
56
57#endif
diff --git a/src/pq/pq_connect.c b/src/pq/pq_connect.c
index 79b9d6107..7599f4b15 100644
--- a/src/pq/pq_connect.c
+++ b/src/pq/pq_connect.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet 2 This file is part of GNUnet
3 Copyright (C) 2017 GNUnet e.V. 3 Copyright (C) 2017, 2019 GNUnet e.V.
4 4
5 GNUnet is free software: you can redistribute it and/or modify it 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 6 under the terms of the GNU Affero General Public License as published
@@ -23,8 +23,7 @@
23 * @author Christian Grothoff 23 * @author Christian Grothoff
24 */ 24 */
25#include "platform.h" 25#include "platform.h"
26#include "gnunet_util_lib.h" 26#include "pq.h"
27#include "gnunet_pq_lib.h"
28 27
29 28
30/** 29/**
@@ -40,12 +39,14 @@ pq_notice_receiver_cb (void *arg,
40 const PGresult *res) 39 const PGresult *res)
41{ 40{
42 /* do nothing, intentionally */ 41 /* do nothing, intentionally */
42 (void) arg;
43 (void) res;
43} 44}
44 45
45 46
46/** 47/**
47 * Function called by libpq whenever it wants to log something. 48 * Function called by libpq whenever it wants to log something.
48 * We log those using the Taler logger. 49 * We log those using the GNUnet logger.
49 * 50 *
50 * @param arg the SQL connection that was used 51 * @param arg the SQL connection that was used
51 * @param message information about some libpq event 52 * @param message information about some libpq event
@@ -54,6 +55,7 @@ static void
54pq_notice_processor_cb (void *arg, 55pq_notice_processor_cb (void *arg,
55 const char *message) 56 const char *message)
56{ 57{
58 (void) arg;
57 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, 59 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
58 "pq", 60 "pq",
59 "%s", 61 "%s",
@@ -64,68 +66,175 @@ pq_notice_processor_cb (void *arg,
64/** 66/**
65 * Create a connection to the Postgres database using @a config_str 67 * Create a connection to the Postgres database using @a config_str
66 * for the configuration. Initialize logging via GNUnet's log 68 * for the configuration. Initialize logging via GNUnet's log
67 * routines and disable Postgres's logger. 69 * routines and disable Postgres's logger. Also ensures that the
70 * statements in @a es are executed whenever we (re)connect to the
71 * database, and that the prepared statements in @a ps are "ready".
72 * If statements in @es fail that were created with
73 * #GNUNET_PQ_make_execute(), then the entire operation fails.
74 *
75 * The caller MUST ensure that @a es and @a ps remain allocated and
76 * initialized in memory until #GNUNET_PQ_disconnect() is called,
77 * as they may be needed repeatedly and no copy will be made.
68 * 78 *
69 * @param config_str configuration to use 79 * @param config_str configuration to use
80 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated
81 * array of statements to execute upon EACH connection, can be NULL
82 * @param ps array of prepared statements to prepare, can be NULL
70 * @return NULL on error 83 * @return NULL on error
71 */ 84 */
72PGconn * 85struct GNUNET_PQ_Context *
73GNUNET_PQ_connect (const char *config_str) 86GNUNET_PQ_connect (const char *config_str,
87 const struct GNUNET_PQ_ExecuteStatement *es,
88 const struct GNUNET_PQ_PreparedStatement *ps)
74{ 89{
75 PGconn *conn; 90 struct GNUNET_PQ_Context *db;
91 unsigned int elen = 0;
92 unsigned int plen = 0;
93
94 if (NULL != es)
95 while (NULL != es[elen].sql)
96 elen++;
97 if (NULL != ps)
98 while (NULL != ps[plen].name)
99 plen++;
100
101 db = GNUNET_new (struct GNUNET_PQ_Context);
102 db->config_str = GNUNET_strdup (config_str);
103 if (0 != elen)
104 {
105 db->es = GNUNET_new_array (elen + 1,
106 struct GNUNET_PQ_ExecuteStatement);
107 memcpy (db->es,
108 es,
109 elen * sizeof (struct GNUNET_PQ_ExecuteStatement));
110 }
111 if (0 != plen)
112 {
113 db->ps = GNUNET_new_array (plen + 1,
114 struct GNUNET_PQ_PreparedStatement);
115 memcpy (db->ps,
116 ps,
117 plen * sizeof (struct GNUNET_PQ_PreparedStatement));
118 }
119 GNUNET_PQ_reconnect (db);
120 if (NULL == db->conn)
121 {
122 GNUNET_free (db->config_str);
123 GNUNET_free (db);
124 return NULL;
125 }
126 return db;
127}
128
76 129
77 conn = PQconnectdb (config_str); 130/**
78 if ((NULL == conn) || 131 * Reinitialize the database @a db.
79 (CONNECTION_OK != 132 *
80 PQstatus (conn))) 133 * @param db database connection to reinitialize
134 */
135void
136GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db)
137{
138 if (NULL != db->conn)
139 PQfinish (db->conn);
140 db->conn = PQconnectdb (db->config_str);
141 if ((NULL == db->conn) ||
142 (CONNECTION_OK != PQstatus (db->conn)))
81 { 143 {
82 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, 144 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
83 "pq", 145 "pq",
84 "Database connection to '%s' failed: %s\n", 146 "Database connection to '%s' failed: %s\n",
85 config_str, 147 db->config_str,
86 (NULL != conn) ? 148 (NULL != db->conn) ?
87 PQerrorMessage (conn) 149 PQerrorMessage (db->conn)
88 : "PQconnectdb returned NULL"); 150 : "PQconnectdb returned NULL");
89 if (NULL != conn) 151 if (NULL != db->conn)
90 PQfinish (conn); 152 {
91 return NULL; 153 PQfinish (db->conn);
154 db->conn = NULL;
155 }
156 return;
92 } 157 }
93 PQsetNoticeReceiver (conn, 158 PQsetNoticeReceiver (db->conn,
94 &pq_notice_receiver_cb, 159 &pq_notice_receiver_cb,
95 conn); 160 db);
96 PQsetNoticeProcessor (conn, 161 PQsetNoticeProcessor (db->conn,
97 &pq_notice_processor_cb, 162 &pq_notice_processor_cb,
98 conn); 163 db);
99 return conn; 164 if ( (NULL != db->es) &&
165 (GNUNET_OK !=
166 GNUNET_PQ_exec_statements (db,
167 db->es)) )
168 {
169 PQfinish (db->conn);
170 db->conn = NULL;
171 return;
172 }
173 if ( (NULL != db->ps) &&
174 (GNUNET_OK !=
175 GNUNET_PQ_prepare_statements (db,
176 db->ps)) )
177 {
178 PQfinish (db->conn);
179 db->conn = NULL;
180 return;
181 }
100} 182}
101 183
102 184
103/** 185/**
104 * Connect to a postgres database using the configuration 186 * Connect to a postgres database using the configuration
105 * option "CONFIG" in @a section. 187 * option "CONFIG" in @a section. Also ensures that the
188 * statements in @a es are executed whenever we (re)connect to the
189 * database, and that the prepared statements in @a ps are "ready".
190 *
191 * The caller MUST ensure that @a es and @a ps remain allocated and
192 * initialized in memory until #GNUNET_PQ_disconnect() is called,
193 * as they may be needed repeatedly and no copy will be made.
106 * 194 *
107 * @param cfg configuration 195 * @param cfg configuration
108 * @param section configuration section to use to get Postgres configuration options 196 * @param section configuration section to use to get Postgres configuration options
197 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated
198 * array of statements to execute upon EACH connection, can be NULL
199 * @param ps array of prepared statements to prepare, can be NULL
109 * @return the postgres handle, NULL on error 200 * @return the postgres handle, NULL on error
110 */ 201 */
111PGconn * 202struct GNUNET_PQ_Context *
112GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, 203GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
113 const char *section) 204 const char *section,
205 const struct GNUNET_PQ_ExecuteStatement *es,
206 const struct GNUNET_PQ_PreparedStatement *ps)
114{ 207{
115 PGconn *dbh; 208 struct GNUNET_PQ_Context *db;
116 char *conninfo; 209 char *conninfo;
117 210
118 /* Open database and precompile statements */
119 if (GNUNET_OK != 211 if (GNUNET_OK !=
120 GNUNET_CONFIGURATION_get_value_string (cfg, 212 GNUNET_CONFIGURATION_get_value_string (cfg,
121 section, 213 section,
122 "CONFIG", 214 "CONFIG",
123 &conninfo)) 215 &conninfo))
124 conninfo = NULL; 216 conninfo = NULL;
125 dbh = GNUNET_PQ_connect (conninfo == NULL ? "" : conninfo); 217 db = GNUNET_PQ_connect (conninfo == NULL ? "" : conninfo,
218 es,
219 ps);
126 GNUNET_free_non_null (conninfo); 220 GNUNET_free_non_null (conninfo);
127 return dbh; 221 return db;
128} 222}
129 223
130 224
225/**
226 * Disconnect from the database, destroying the prepared statements
227 * and releasing other associated resources.
228 *
229 * @param db database handle to disconnect (will be free'd)
230 */
231void
232GNUNET_PQ_disconnect (struct GNUNET_PQ_Context *db)
233{
234 GNUNET_free_non_null (db->es);
235 GNUNET_free_non_null (db->ps);
236 PQfinish (db->conn);
237 GNUNET_free (db);
238}
239
131/* end of pq/pq_connect.c */ 240/* end of pq/pq_connect.c */
diff --git a/src/pq/pq_eval.c b/src/pq/pq_eval.c
index 1d041f226..5bcf8ca0e 100644
--- a/src/pq/pq_eval.c
+++ b/src/pq/pq_eval.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet 2 This file is part of GNUnet
3 Copyright (C) 2017 GNUnet e.V. 3 Copyright (C) 2017, 2019 GNUnet e.V.
4 4
5 GNUnet is free software: you can redistribute it and/or modify it 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 6 under the terms of the GNU Affero General Public License as published
@@ -23,8 +23,7 @@
23 * @author Christian Grothoff 23 * @author Christian Grothoff
24 */ 24 */
25#include "platform.h" 25#include "platform.h"
26#include "gnunet_util_lib.h" 26#include "pq.h"
27#include "gnunet_pq_lib.h"
28 27
29 28
30/** 29/**
@@ -47,7 +46,7 @@
47 * Check the @a result's error code to see what happened. 46 * Check the @a result's error code to see what happened.
48 * Also logs errors. 47 * Also logs errors.
49 * 48 *
50 * @param connection connection to execute the statement in 49 * @param db database to execute the statement with
51 * @param statement_name name of the statement that created @a result 50 * @param statement_name name of the statement that created @a result
52 * @param result result to check 51 * @param result result to check
53 * @return status code from the result, mapping PQ status 52 * @return status code from the result, mapping PQ status
@@ -57,17 +56,31 @@
57 * @deprecated (low level, let's see if we can do with just the high-level functions) 56 * @deprecated (low level, let's see if we can do with just the high-level functions)
58 */ 57 */
59enum GNUNET_DB_QueryStatus 58enum GNUNET_DB_QueryStatus
60GNUNET_PQ_eval_result (PGconn *connection, 59GNUNET_PQ_eval_result (struct GNUNET_PQ_Context *db,
61 const char *statement_name, 60 const char *statement_name,
62 PGresult *result) 61 PGresult *result)
63{ 62{
64 ExecStatusType est; 63 ExecStatusType est;
65 64
65 if (NULL == result)
66 return GNUNET_DB_STATUS_SOFT_ERROR;
66 est = PQresultStatus (result); 67 est = PQresultStatus (result);
67 if ((PGRES_COMMAND_OK != est) && 68 if ((PGRES_COMMAND_OK != est) &&
68 (PGRES_TUPLES_OK != est)) 69 (PGRES_TUPLES_OK != est))
69 { 70 {
70 const char *sqlstate; 71 const char *sqlstate;
72 ConnStatusType status;
73
74 if (CONNECTION_OK != (status = PQstatus (db->conn)))
75 {
76 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
77 "pq",
78 "Database connection failed during query `%s': %d (reconnecting)\n",
79 statement_name,
80 status);
81 GNUNET_PQ_reconnect (db);
82 return GNUNET_DB_STATUS_SOFT_ERROR;
83 }
71 84
72 sqlstate = PQresultErrorField (result, 85 sqlstate = PQresultErrorField (result,
73 PG_DIAG_SQLSTATE); 86 PG_DIAG_SQLSTATE);
@@ -94,7 +107,7 @@ GNUNET_PQ_eval_result (PGconn *connection,
94 PG_DIAG_MESSAGE_DETAIL), 107 PG_DIAG_MESSAGE_DETAIL),
95 PQresultErrorMessage (result), 108 PQresultErrorMessage (result),
96 PQresStatus (PQresultStatus (result)), 109 PQresStatus (PQresultStatus (result)),
97 PQerrorMessage (connection)); 110 PQerrorMessage (db->conn));
98 return GNUNET_DB_STATUS_SOFT_ERROR; 111 return GNUNET_DB_STATUS_SOFT_ERROR;
99 } 112 }
100 if (0 == strcmp (sqlstate, 113 if (0 == strcmp (sqlstate,
@@ -111,7 +124,7 @@ GNUNET_PQ_eval_result (PGconn *connection,
111 PG_DIAG_MESSAGE_DETAIL), 124 PG_DIAG_MESSAGE_DETAIL),
112 PQresultErrorMessage (result), 125 PQresultErrorMessage (result),
113 PQresStatus (PQresultStatus (result)), 126 PQresStatus (PQresultStatus (result)),
114 PQerrorMessage (connection)); 127 PQerrorMessage (db->conn));
115 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 128 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
116 } 129 }
117 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, 130 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
@@ -124,7 +137,7 @@ GNUNET_PQ_eval_result (PGconn *connection,
124 PG_DIAG_MESSAGE_DETAIL), 137 PG_DIAG_MESSAGE_DETAIL),
125 PQresultErrorMessage (result), 138 PQresultErrorMessage (result),
126 PQresStatus (PQresultStatus (result)), 139 PQresStatus (PQresultStatus (result)),
127 PQerrorMessage (connection)); 140 PQerrorMessage (db->conn));
128 return GNUNET_DB_STATUS_HARD_ERROR; 141 return GNUNET_DB_STATUS_HARD_ERROR;
129 } 142 }
130 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 143 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
@@ -136,7 +149,7 @@ GNUNET_PQ_eval_result (PGconn *connection,
136 * statement in @a connnection using the given @a params. Returns the 149 * statement in @a connnection using the given @a params. Returns the
137 * resulting session state. 150 * resulting session state.
138 * 151 *
139 * @param connection connection to execute the statement in 152 * @param db database to execute the statement with
140 * @param statement_name name of the statement 153 * @param statement_name name of the statement
141 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated) 154 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
142 * @return status code from the result, mapping PQ status 155 * @return status code from the result, mapping PQ status
@@ -148,17 +161,19 @@ GNUNET_PQ_eval_result (PGconn *connection,
148 * zero; if INSERT was successful, we return one. 161 * zero; if INSERT was successful, we return one.
149 */ 162 */
150enum GNUNET_DB_QueryStatus 163enum GNUNET_DB_QueryStatus
151GNUNET_PQ_eval_prepared_non_select (PGconn *connection, 164GNUNET_PQ_eval_prepared_non_select (struct GNUNET_PQ_Context *db,
152 const char *statement_name, 165 const char *statement_name,
153 const struct GNUNET_PQ_QueryParam *params) 166 const struct GNUNET_PQ_QueryParam *params)
154{ 167{
155 PGresult *result; 168 PGresult *result;
156 enum GNUNET_DB_QueryStatus qs; 169 enum GNUNET_DB_QueryStatus qs;
157 170
158 result = GNUNET_PQ_exec_prepared (connection, 171 result = GNUNET_PQ_exec_prepared (db,
159 statement_name, 172 statement_name,
160 params); 173 params);
161 qs = GNUNET_PQ_eval_result (connection, 174 if (NULL == result)
175 return GNUNET_DB_STATUS_SOFT_ERROR;
176 qs = GNUNET_PQ_eval_result (db,
162 statement_name, 177 statement_name,
163 result); 178 result);
164 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 179 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
@@ -182,7 +197,7 @@ GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
182 * status including the number of results given to @a rh (possibly zero). 197 * status including the number of results given to @a rh (possibly zero).
183 * @a rh will not have been called if the return value is negative. 198 * @a rh will not have been called if the return value is negative.
184 * 199 *
185 * @param connection connection to execute the statement in 200 * @param db database to execute the statement with
186 * @param statement_name name of the statement 201 * @param statement_name name of the statement
187 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated) 202 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
188 * @param rh function to call with the result set, NULL to ignore 203 * @param rh function to call with the result set, NULL to ignore
@@ -191,7 +206,7 @@ GNUNET_PQ_eval_prepared_non_select (PGconn *connection,
191 * codes to `enum GNUNET_DB_QueryStatus`. 206 * codes to `enum GNUNET_DB_QueryStatus`.
192 */ 207 */
193enum GNUNET_DB_QueryStatus 208enum GNUNET_DB_QueryStatus
194GNUNET_PQ_eval_prepared_multi_select (PGconn *connection, 209GNUNET_PQ_eval_prepared_multi_select (struct GNUNET_PQ_Context *db,
195 const char *statement_name, 210 const char *statement_name,
196 const struct GNUNET_PQ_QueryParam *params, 211 const struct GNUNET_PQ_QueryParam *params,
197 GNUNET_PQ_PostgresResultHandler rh, 212 GNUNET_PQ_PostgresResultHandler rh,
@@ -201,10 +216,12 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
201 enum GNUNET_DB_QueryStatus qs; 216 enum GNUNET_DB_QueryStatus qs;
202 unsigned int ret; 217 unsigned int ret;
203 218
204 result = GNUNET_PQ_exec_prepared (connection, 219 result = GNUNET_PQ_exec_prepared (db,
205 statement_name, 220 statement_name,
206 params); 221 params);
207 qs = GNUNET_PQ_eval_result (connection, 222 if (NULL == result)
223 return GNUNET_DB_STATUS_SOFT_ERROR;
224 qs = GNUNET_PQ_eval_result (db,
208 statement_name, 225 statement_name,
209 result); 226 result);
210 if (qs < 0) 227 if (qs < 0)
@@ -230,7 +247,7 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
230 * value was #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT. Returns the 247 * value was #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT. Returns the
231 * resulting session status. 248 * resulting session status.
232 * 249 *
233 * @param connection connection to execute the statement in 250 * @param db database to execute the statement with
234 * @param statement_name name of the statement 251 * @param statement_name name of the statement
235 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated) 252 * @param params parameters to give to the statement (#GNUNET_PQ_query_param_end-terminated)
236 * @param[in,out] rs result specification to use for storing the result of the query 253 * @param[in,out] rs result specification to use for storing the result of the query
@@ -238,7 +255,7 @@ GNUNET_PQ_eval_prepared_multi_select (PGconn *connection,
238 * codes to `enum GNUNET_DB_QueryStatus`. 255 * codes to `enum GNUNET_DB_QueryStatus`.
239 */ 256 */
240enum GNUNET_DB_QueryStatus 257enum GNUNET_DB_QueryStatus
241GNUNET_PQ_eval_prepared_singleton_select (PGconn *connection, 258GNUNET_PQ_eval_prepared_singleton_select (struct GNUNET_PQ_Context *db,
242 const char *statement_name, 259 const char *statement_name,
243 const struct 260 const struct
244 GNUNET_PQ_QueryParam *params, 261 GNUNET_PQ_QueryParam *params,
@@ -247,10 +264,12 @@ GNUNET_PQ_eval_prepared_singleton_select (PGconn *connection,
247 PGresult *result; 264 PGresult *result;
248 enum GNUNET_DB_QueryStatus qs; 265 enum GNUNET_DB_QueryStatus qs;
249 266
250 result = GNUNET_PQ_exec_prepared (connection, 267 result = GNUNET_PQ_exec_prepared (db,
251 statement_name, 268 statement_name,
252 params); 269 params);
253 qs = GNUNET_PQ_eval_result (connection, 270 if (NULL == result)
271 return GNUNET_DB_STATUS_SOFT_ERROR;
272 qs = GNUNET_PQ_eval_result (db,
254 statement_name, 273 statement_name,
255 result); 274 result);
256 if (qs < 0) 275 if (qs < 0)
diff --git a/src/pq/pq_exec.c b/src/pq/pq_exec.c
index 00527151a..fd4feae53 100644
--- a/src/pq/pq_exec.c
+++ b/src/pq/pq_exec.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet 2 This file is part of GNUnet
3 Copyright (C) 2017 GNUnet e.V. 3 Copyright (C) 2017, 2019 GNUnet e.V.
4 4
5 GNUnet is free software: you can redistribute it and/or modify it 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 6 under the terms of the GNU Affero General Public License as published
@@ -23,8 +23,7 @@
23 * @author Christian Grothoff 23 * @author Christian Grothoff
24 */ 24 */
25#include "platform.h" 25#include "platform.h"
26#include "gnunet_util_lib.h" 26#include "pq.h"
27#include "gnunet_pq_lib.h"
28 27
29 28
30/** 29/**
@@ -67,14 +66,14 @@ GNUNET_PQ_make_try_execute (const char *sql)
67/** 66/**
68 * Request execution of an array of statements @a es from Postgres. 67 * Request execution of an array of statements @a es from Postgres.
69 * 68 *
70 * @param connection connection to execute the statements over 69 * @param db database to execute the statements with
71 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared 70 * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
72 * statements. 71 * statements.
73 * @return #GNUNET_OK on success (modulo statements where errors can be ignored) 72 * @return #GNUNET_OK on success (modulo statements where errors can be ignored)
74 * #GNUNET_SYSERR on error 73 * #GNUNET_SYSERR on error
75 */ 74 */
76int 75int
77GNUNET_PQ_exec_statements (PGconn *connection, 76GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db,
78 const struct GNUNET_PQ_ExecuteStatement *es) 77 const struct GNUNET_PQ_ExecuteStatement *es)
79{ 78{
80 for (unsigned int i = 0; NULL != es[i].sql; i++) 79 for (unsigned int i = 0; NULL != es[i].sql; i++)
@@ -84,8 +83,8 @@ GNUNET_PQ_exec_statements (PGconn *connection,
84 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 83 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
85 "Running statement `%s' on %p\n", 84 "Running statement `%s' on %p\n",
86 es[i].sql, 85 es[i].sql,
87 connection); 86 db);
88 result = PQexec (connection, 87 result = PQexec (db->conn,
89 es[i].sql); 88 es[i].sql);
90 if ((GNUNET_NO == es[i].ignore_errors) && 89 if ((GNUNET_NO == es[i].ignore_errors) &&
91 (PGRES_COMMAND_OK != PQresultStatus (result))) 90 (PGRES_COMMAND_OK != PQresultStatus (result)))
@@ -100,7 +99,7 @@ GNUNET_PQ_exec_statements (PGconn *connection,
100 PG_DIAG_MESSAGE_DETAIL), 99 PG_DIAG_MESSAGE_DETAIL),
101 PQresultErrorMessage (result), 100 PQresultErrorMessage (result),
102 PQresStatus (PQresultStatus (result)), 101 PQresStatus (PQresultStatus (result)),
103 PQerrorMessage (connection)); 102 PQerrorMessage (db->conn));
104 PQclear (result); 103 PQclear (result);
105 return GNUNET_SYSERR; 104 return GNUNET_SYSERR;
106 } 105 }
diff --git a/src/pq/pq_prepare.c b/src/pq/pq_prepare.c
index 0facf100f..b7003fb69 100644
--- a/src/pq/pq_prepare.c
+++ b/src/pq/pq_prepare.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet 2 This file is part of GNUnet
3 Copyright (C) 2017 GNUnet e.V. 3 Copyright (C) 2017, 2019 GNUnet e.V.
4 4
5 GNUnet is free software: you can redistribute it and/or modify it 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 6 under the terms of the GNU Affero General Public License as published
@@ -23,8 +23,7 @@
23 * @author Christian Grothoff 23 * @author Christian Grothoff
24 */ 24 */
25#include "platform.h" 25#include "platform.h"
26#include "gnunet_util_lib.h" 26#include "pq.h"
27#include "gnunet_pq_lib.h"
28 27
29 28
30/** 29/**
@@ -53,16 +52,42 @@ GNUNET_PQ_make_prepare (const char *name,
53/** 52/**
54 * Request creation of prepared statements @a ps from Postgres. 53 * Request creation of prepared statements @a ps from Postgres.
55 * 54 *
56 * @param connection connection to prepare the statements for 55 * @param db database to prepare the statements for
57 * @param ps #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared 56 * @param ps #GNUNET_PQ_PREPARED_STATEMENT_END-terminated array of prepared
58 * statements. 57 * statements.
59 * @return #GNUNET_OK on success, 58 * @return #GNUNET_OK on success,
60 * #GNUNET_SYSERR on error 59 * #GNUNET_SYSERR on error
61 */ 60 */
62int 61int
63GNUNET_PQ_prepare_statements (PGconn *connection, 62GNUNET_PQ_prepare_statements (struct GNUNET_PQ_Context *db,
64 const struct GNUNET_PQ_PreparedStatement *ps) 63 const struct GNUNET_PQ_PreparedStatement *ps)
65{ 64{
65 if (db->ps != ps)
66 {
67 /* add 'ps' to list db->ps of prepared statements to run on reconnect! */
68 unsigned int olen = 0; /* length of existing 'db->ps' array */
69 unsigned int nlen = 0; /* length of 'ps' array */
70 struct GNUNET_PQ_PreparedStatement *rps; /* combined array */
71
72 if (NULL != db->ps)
73 while (NULL != db->ps[olen].name)
74 olen++;
75 while (NULL != ps[nlen].name)
76 nlen++;
77 rps = GNUNET_new_array (olen + nlen + 1,
78 struct GNUNET_PQ_PreparedStatement);
79 if (NULL != db->ps)
80 memcpy (rps,
81 db->ps,
82 olen * sizeof (struct GNUNET_PQ_PreparedStatement));
83 memcpy (&rps[olen],
84 ps,
85 nlen * sizeof (struct GNUNET_PQ_PreparedStatement));
86 GNUNET_free_non_null (db->ps);
87 db->ps = rps;
88 }
89
90 /* actually prepare statements */
66 for (unsigned int i = 0; NULL != ps[i].name; i++) 91 for (unsigned int i = 0; NULL != ps[i].name; i++)
67 { 92 {
68 PGresult *ret; 93 PGresult *ret;
@@ -72,7 +97,7 @@ GNUNET_PQ_prepare_statements (PGconn *connection,
72 "Preparing SQL statement `%s' as `%s'\n", 97 "Preparing SQL statement `%s' as `%s'\n",
73 ps[i].sql, 98 ps[i].sql,
74 ps[i].name); 99 ps[i].name);
75 ret = PQprepare (connection, 100 ret = PQprepare (db->conn,
76 ps[i].name, 101 ps[i].name,
77 ps[i].sql, 102 ps[i].sql,
78 ps[i].num_arguments, 103 ps[i].num_arguments,
@@ -84,7 +109,7 @@ GNUNET_PQ_prepare_statements (PGconn *connection,
84 _ ("PQprepare (`%s' as `%s') failed with error: %s\n"), 109 _ ("PQprepare (`%s' as `%s') failed with error: %s\n"),
85 ps[i].sql, 110 ps[i].sql,
86 ps[i].name, 111 ps[i].name,
87 PQerrorMessage (connection)); 112 PQerrorMessage (db->conn));
88 PQclear (ret); 113 PQclear (ret);
89 return GNUNET_SYSERR; 114 return GNUNET_SYSERR;
90 } 115 }
diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c
index cfb16ac12..1fb1e71c0 100644
--- a/src/pq/pq_result_helper.c
+++ b/src/pq/pq_result_helper.c
@@ -587,7 +587,7 @@ extract_abs_time (void *cls,
587 res = (int64_t *) PQgetvalue (result, 587 res = (int64_t *) PQgetvalue (result,
588 row, 588 row,
589 fnum); 589 fnum);
590 if (INT64_MAX == *res) 590 if (INT64_MAX == GNUNET_ntohll ((uint64_t) *res))
591 *udst = GNUNET_TIME_UNIT_FOREVER_ABS; 591 *udst = GNUNET_TIME_UNIT_FOREVER_ABS;
592 else 592 else
593 udst->abs_value_us = GNUNET_ntohll ((uint64_t) *res); 593 udst->abs_value_us = GNUNET_ntohll ((uint64_t) *res);
diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c
index 697d8e580..a103aca5d 100644
--- a/src/pq/test_pq.c
+++ b/src/pq/test_pq.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet 2 This file is part of GNUnet
3 (C) 2015, 2016 GNUnet e.V. 3 (C) 2015, 2016, 2019 GNUnet e.V.
4 4
5 GNUnet is free software: you can redistribute it and/or modify it 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 6 under the terms of the GNU Affero General Public License as published
@@ -23,75 +23,65 @@
23 * @author Christian Grothoff <christian@grothoff.org> 23 * @author Christian Grothoff <christian@grothoff.org>
24 */ 24 */
25#include "platform.h" 25#include "platform.h"
26#include "gnunet_util_lib.h" 26#include "pq.h"
27#include "gnunet_pq_lib.h"
28 27
29 28
30/** 29/**
31 * Setup prepared statements. 30 * Setup prepared statements.
32 * 31 *
33 * @param db_conn connection handle to initialize 32 * @param db database handle to initialize
34 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure 33 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
35 */ 34 */
36static int 35static int
37postgres_prepare (PGconn *db_conn) 36postgres_prepare (struct GNUNET_PQ_Context *db)
38{ 37{
39 PGresult *result; 38 struct GNUNET_PQ_PreparedStatement ps[] = {
40 39 GNUNET_PQ_make_prepare ("test_insert",
41#define PREPARE(name, sql, ...) \ 40 "INSERT INTO test_pq ("
42 do { \ 41 " pub"
43 result = PQprepare (db_conn, name, sql, __VA_ARGS__); \ 42 ",sig"
44 if (PGRES_COMMAND_OK != PQresultStatus (result)) \ 43 ",abs_time"
45 { \ 44 ",forever"
46 GNUNET_break (0); \ 45 ",hash"
47 PQclear (result); result = NULL; \ 46 ",vsize"
48 return GNUNET_SYSERR; \ 47 ",u16"
49 } \ 48 ",u32"
50 PQclear (result); result = NULL; \ 49 ",u64"
51 } while (0); 50 ") VALUES "
51 "($1, $2, $3, $4, $5, $6,"
52 "$7, $8, $9);",
53 9),
54 GNUNET_PQ_make_prepare ("test_select",
55 "SELECT"
56 " pub"
57 ",sig"
58 ",abs_time"
59 ",forever"
60 ",hash"
61 ",vsize"
62 ",u16"
63 ",u32"
64 ",u64"
65 " FROM test_pq"
66 " ORDER BY abs_time DESC "
67 " LIMIT 1;",
68 0),
69 GNUNET_PQ_PREPARED_STATEMENT_END
70 };
52 71
53 PREPARE ("test_insert", 72 return GNUNET_PQ_prepare_statements (db,
54 "INSERT INTO test_pq (" 73 ps);
55 " pub"
56 ",sig"
57 ",abs_time"
58 ",forever"
59 ",hash"
60 ",vsize"
61 ",u16"
62 ",u32"
63 ",u64"
64 ") VALUES "
65 "($1, $2, $3, $4, $5, $6,"
66 "$7, $8, $9);",
67 9, NULL);
68 PREPARE ("test_select",
69 "SELECT"
70 " pub"
71 ",sig"
72 ",abs_time"
73 ",forever"
74 ",hash"
75 ",vsize"
76 ",u16"
77 ",u32"
78 ",u64"
79 " FROM test_pq"
80 " ORDER BY abs_time DESC "
81 " LIMIT 1;",
82 0, NULL);
83 return GNUNET_OK;
84#undef PREPARE
85} 74}
86 75
87 76
88/** 77/**
89 * Run actual test queries. 78 * Run actual test queries.
90 * 79 *
80 * @param db database handle
91 * @return 0 on success 81 * @return 0 on success
92 */ 82 */
93static int 83static int
94run_queries (PGconn *conn) 84run_queries (struct GNUNET_PQ_Context *db)
95{ 85{
96 struct GNUNET_CRYPTO_RsaPublicKey *pub; 86 struct GNUNET_CRYPTO_RsaPublicKey *pub;
97 struct GNUNET_CRYPTO_RsaPublicKey *pub2 = NULL; 87 struct GNUNET_CRYPTO_RsaPublicKey *pub2 = NULL;
@@ -155,7 +145,7 @@ run_queries (PGconn *conn)
155 GNUNET_PQ_result_spec_end 145 GNUNET_PQ_result_spec_end
156 }; 146 };
157 147
158 result = GNUNET_PQ_exec_prepared (conn, 148 result = GNUNET_PQ_exec_prepared (db,
159 "test_insert", 149 "test_insert",
160 params_insert); 150 params_insert);
161 if (PGRES_COMMAND_OK != PQresultStatus (result)) 151 if (PGRES_COMMAND_OK != PQresultStatus (result))
@@ -171,7 +161,7 @@ run_queries (PGconn *conn)
171 } 161 }
172 162
173 PQclear (result); 163 PQclear (result);
174 result = GNUNET_PQ_exec_prepared (conn, 164 result = GNUNET_PQ_exec_prepared (db,
175 "test_select", 165 "test_select",
176 params_select); 166 params_select);
177 if (1 != 167 if (1 !=
@@ -224,67 +214,71 @@ int
224main (int argc, 214main (int argc,
225 const char *const argv[]) 215 const char *const argv[])
226{ 216{
227 PGconn *conn; 217 struct GNUNET_PQ_ExecuteStatement es[] = {
228 PGresult *result; 218 GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS test_pq ("
219 " pub BYTEA NOT NULL"
220 ",sig BYTEA NOT NULL"
221 ",abs_time INT8 NOT NULL"
222 ",forever INT8 NOT NULL"
223 ",hash BYTEA NOT NULL CHECK(LENGTH(hash)=64)"
224 ",vsize VARCHAR NOT NULL"
225 ",u16 INT2 NOT NULL"
226 ",u32 INT4 NOT NULL"
227 ",u64 INT8 NOT NULL"
228 ")"),
229 GNUNET_PQ_EXECUTE_STATEMENT_END
230 };
231 struct GNUNET_PQ_Context *db;
229 int ret; 232 int ret;
230 233
231 GNUNET_log_setup ("test-pq", 234 GNUNET_log_setup ("test-pq",
232 "WARNING", 235 "WARNING",
233 NULL); 236 NULL);
234 conn = PQconnectdb ("postgres:///gnunetcheck"); 237 db = GNUNET_PQ_connect ("postgres:///gnunetcheck",
235 if (CONNECTION_OK != PQstatus (conn)) 238 es,
239 NULL);
240 if (CONNECTION_OK != PQstatus (db->conn))
236 { 241 {
237 fprintf (stderr, 242 fprintf (stderr,
238 "Cannot run test, database connection failed: %s\n", 243 "Cannot run test, database connection failed: %s\n",
239 PQerrorMessage (conn)); 244 PQerrorMessage (db->conn));
240 GNUNET_break (0); 245 GNUNET_break (0);
241 PQfinish (conn); 246 GNUNET_PQ_disconnect (db);
242 return 77; /* signal test was skipped */ 247 return 77; /* signal test was skipped */
243 } 248 }
244
245 result = PQexec (conn,
246 "CREATE TEMPORARY TABLE IF NOT EXISTS test_pq ("
247 " pub BYTEA NOT NULL"
248 ",sig BYTEA NOT NULL"
249 ",abs_time INT8 NOT NULL"
250 ",forever INT8 NOT NULL"
251 ",hash BYTEA NOT NULL CHECK(LENGTH(hash)=64)"
252 ",vsize VARCHAR NOT NULL"
253 ",u16 INT2 NOT NULL"
254 ",u32 INT4 NOT NULL"
255 ",u64 INT8 NOT NULL"
256 ")");
257 if (PGRES_COMMAND_OK != PQresultStatus (result))
258 {
259 fprintf (stderr,
260 "Failed to create table: %s\n",
261 PQerrorMessage (conn));
262 PQclear (result);
263 PQfinish (conn);
264 return 1;
265 }
266 PQclear (result);
267 if (GNUNET_OK != 249 if (GNUNET_OK !=
268 postgres_prepare (conn)) 250 postgres_prepare (db))
269 { 251 {
270 GNUNET_break (0); 252 GNUNET_break (0);
271 PQfinish (conn); 253 GNUNET_PQ_disconnect (db);
272 return 1; 254 return 1;
273 } 255 }
274 ret = run_queries (conn); 256 ret = run_queries (db);
275 result = PQexec (conn, 257#if TEST_RESTART
276 "DROP TABLE test_pq"); 258 fprintf (stderr, "Please restart Postgres database now!\n");
277 if (PGRES_COMMAND_OK != PQresultStatus (result)) 259 sleep (60);
260 ret = run_queries (db);
261 fprintf (stderr, "Result: %d (expect: 1 -- if you restarted the DB)\n", ret);
262 ret = run_queries (db);
263 fprintf (stderr, "Result: %d (expect: 0)\n", ret);
264#endif
278 { 265 {
279 fprintf (stderr, 266 struct GNUNET_PQ_ExecuteStatement es[] = {
280 "Failed to create table: %s\n", 267 GNUNET_PQ_make_execute ("DROP TABLE test_pq"),
281 PQerrorMessage (conn)); 268 GNUNET_PQ_EXECUTE_STATEMENT_END
282 PQclear (result); 269 };
283 PQfinish (conn); 270
284 return 1; 271 if (GNUNET_OK !=
272 GNUNET_PQ_exec_statements (db,
273 es))
274 {
275 fprintf (stderr,
276 "Failed to drop table\n");
277 GNUNET_PQ_disconnect (db);
278 return 1;
279 }
285 } 280 }
286 PQclear (result); 281 GNUNET_PQ_disconnect (db);
287 PQfinish (conn);
288 return ret; 282 return ret;
289} 283}
290 284