diff options
-rw-r--r-- | src/include/gnunet_pq_lib.h | 102 | ||||
-rw-r--r-- | src/pq/pq.h | 5 | ||||
-rw-r--r-- | src/pq/pq_connect.c | 158 | ||||
-rw-r--r-- | src/pq/pq_exec.c | 2 |
4 files changed, 201 insertions, 66 deletions
diff --git a/src/include/gnunet_pq_lib.h b/src/include/gnunet_pq_lib.h index fe3fabbea..6a2227581 100644 --- a/src/include/gnunet_pq_lib.h +++ b/src/include/gnunet_pq_lib.h | |||
@@ -470,7 +470,8 @@ GNUNET_PQ_result_spec_absolute_time_nbo (const char *name, | |||
470 | * @return array entry for the result specification to use | 470 | * @return array entry for the result specification to use |
471 | */ | 471 | */ |
472 | struct GNUNET_PQ_ResultSpec | 472 | struct GNUNET_PQ_ResultSpec |
473 | GNUNET_PQ_result_spec_uint16 (const char *name, uint16_t *u16); | 473 | GNUNET_PQ_result_spec_uint16 (const char *name, |
474 | uint16_t *u16); | ||
474 | 475 | ||
475 | 476 | ||
476 | /** | 477 | /** |
@@ -481,7 +482,8 @@ GNUNET_PQ_result_spec_uint16 (const char *name, uint16_t *u16); | |||
481 | * @return array entry for the result specification to use | 482 | * @return array entry for the result specification to use |
482 | */ | 483 | */ |
483 | struct GNUNET_PQ_ResultSpec | 484 | struct GNUNET_PQ_ResultSpec |
484 | GNUNET_PQ_result_spec_uint32 (const char *name, uint32_t *u32); | 485 | GNUNET_PQ_result_spec_uint32 (const char *name, |
486 | uint32_t *u32); | ||
485 | 487 | ||
486 | 488 | ||
487 | /** | 489 | /** |
@@ -492,7 +494,8 @@ GNUNET_PQ_result_spec_uint32 (const char *name, uint32_t *u32); | |||
492 | * @return array entry for the result specification to use | 494 | * @return array entry for the result specification to use |
493 | */ | 495 | */ |
494 | struct GNUNET_PQ_ResultSpec | 496 | struct GNUNET_PQ_ResultSpec |
495 | GNUNET_PQ_result_spec_uint64 (const char *name, uint64_t *u64); | 497 | GNUNET_PQ_result_spec_uint64 (const char *name, |
498 | uint64_t *u64); | ||
496 | 499 | ||
497 | 500 | ||
498 | /* ************************* pq.c functions ************************ */ | 501 | /* ************************* pq.c functions ************************ */ |
@@ -641,11 +644,11 @@ GNUNET_PQ_eval_prepared_multi_select (struct GNUNET_PQ_Context *db, | |||
641 | * codes to `enum GNUNET_DB_QueryStatus`. | 644 | * codes to `enum GNUNET_DB_QueryStatus`. |
642 | */ | 645 | */ |
643 | enum GNUNET_DB_QueryStatus | 646 | enum GNUNET_DB_QueryStatus |
644 | GNUNET_PQ_eval_prepared_singleton_select (struct GNUNET_PQ_Context *db, | 647 | GNUNET_PQ_eval_prepared_singleton_select ( |
645 | const char *statement_name, | 648 | struct GNUNET_PQ_Context *db, |
646 | const struct | 649 | const char *statement_name, |
647 | GNUNET_PQ_QueryParam *params, | 650 | const struct GNUNET_PQ_QueryParam *params, |
648 | struct GNUNET_PQ_ResultSpec *rs); | 651 | struct GNUNET_PQ_ResultSpec *rs); |
649 | 652 | ||
650 | 653 | ||
651 | /* ******************** pq_prepare.c functions ************** */ | 654 | /* ******************** pq_prepare.c functions ************** */ |
@@ -772,7 +775,7 @@ GNUNET_PQ_make_try_execute (const char *sql); | |||
772 | * @return #GNUNET_OK on success (modulo statements where errors can be ignored) | 775 | * @return #GNUNET_OK on success (modulo statements where errors can be ignored) |
773 | * #GNUNET_SYSERR on error | 776 | * #GNUNET_SYSERR on error |
774 | */ | 777 | */ |
775 | int | 778 | enum GNUNET_GenericReturnValue |
776 | GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db, | 779 | GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db, |
777 | const struct GNUNET_PQ_ExecuteStatement *es); | 780 | const struct GNUNET_PQ_ExecuteStatement *es); |
778 | 781 | ||
@@ -781,6 +784,29 @@ GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db, | |||
781 | 784 | ||
782 | 785 | ||
783 | /** | 786 | /** |
787 | * Flags to control PQ operation. | ||
788 | */ | ||
789 | enum GNUNET_PQ_Options | ||
790 | { | ||
791 | /** | ||
792 | * Traditional default. Do nothing special. | ||
793 | */ | ||
794 | GNUNET_PQ_FLAG_NONE = 0, | ||
795 | |||
796 | /** | ||
797 | * Dropping database. Do not attempt to initialize | ||
798 | * versioning schema if not present. | ||
799 | */ | ||
800 | GNUNET_PQ_FLAG_DROP = 1, | ||
801 | |||
802 | /** | ||
803 | * Check database version is current. Fail to connect if it is not. | ||
804 | */ | ||
805 | GNUNET_PQ_FLAG_CHECK_CURRENT = 2 | ||
806 | }; | ||
807 | |||
808 | |||
809 | /** | ||
784 | * Create a connection to the Postgres database using @a config_str for the | 810 | * Create a connection to the Postgres database using @a config_str for the |
785 | * configuration. Initialize logging via GNUnet's log routines and disable | 811 | * configuration. Initialize logging via GNUnet's log routines and disable |
786 | * Postgres's logger. Also ensures that the statements in @a load_path and @a | 812 | * Postgres's logger. Also ensures that the statements in @a load_path and @a |
@@ -810,6 +836,37 @@ GNUNET_PQ_connect (const char *config_str, | |||
810 | 836 | ||
811 | 837 | ||
812 | /** | 838 | /** |
839 | * Create a connection to the Postgres database using @a config_str for the | ||
840 | * configuration. Initialize logging via GNUnet's log routines and disable | ||
841 | * Postgres's logger. Also ensures that the statements in @a load_path and @a | ||
842 | * es are executed whenever we (re)connect to the database, and that the | ||
843 | * prepared statements in @a ps are "ready". If statements in @es fail that | ||
844 | * were created with #GNUNET_PQ_make_execute(), then the entire operation | ||
845 | * fails. | ||
846 | * | ||
847 | * In @a load_path, a list of "$XXXX.sql" files is expected where $XXXX | ||
848 | * must be a sequence of contiguous integer values starting at 0000. | ||
849 | * These files are then loaded in sequence using "psql $config_str" before | ||
850 | * running statements from @e es. The directory is inspected again on | ||
851 | * reconnect. | ||
852 | * | ||
853 | * @param config_str configuration to use | ||
854 | * @param load_path path to directory with SQL transactions to run, can be NULL | ||
855 | * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated | ||
856 | * array of statements to execute upon EACH connection, can be NULL | ||
857 | * @param ps array of prepared statements to prepare, can be NULL | ||
858 | * @param flags connection flags | ||
859 | * @return NULL on error | ||
860 | */ | ||
861 | struct GNUNET_PQ_Context * | ||
862 | GNUNET_PQ_connect2 (const char *config_str, | ||
863 | const char *load_path, | ||
864 | const struct GNUNET_PQ_ExecuteStatement *es, | ||
865 | const struct GNUNET_PQ_PreparedStatement *ps, | ||
866 | enum GNUNET_PQ_Options flags); | ||
867 | |||
868 | |||
869 | /** | ||
813 | * Connect to a postgres database using the configuration | 870 | * Connect to a postgres database using the configuration |
814 | * option "CONFIG" in @a section. Also ensures that the | 871 | * option "CONFIG" in @a section. Also ensures that the |
815 | * statements in @a es are executed whenever we (re)connect to the | 872 | * statements in @a es are executed whenever we (re)connect to the |
@@ -835,6 +892,33 @@ GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, | |||
835 | 892 | ||
836 | 893 | ||
837 | /** | 894 | /** |
895 | * Connect to a postgres database using the configuration | ||
896 | * option "CONFIG" in @a section. Also ensures that the | ||
897 | * statements in @a es are executed whenever we (re)connect to the | ||
898 | * database, and that the prepared statements in @a ps are "ready". | ||
899 | * | ||
900 | * The caller does not have to ensure that @a es and @a ps remain allocated | ||
901 | * and initialized in memory until #GNUNET_PQ_disconnect() is called, as a copy will be made. | ||
902 | * | ||
903 | * @param cfg configuration | ||
904 | * @param section configuration section to use to get Postgres configuration options | ||
905 | * @param load_path_suffix suffix to append to the SQL_DIR in the configuration | ||
906 | * @param es #GNUNET_PQ_PREPARED_STATEMENT_END-terminated | ||
907 | * array of statements to execute upon EACH connection, can be NULL | ||
908 | * @param ps array of prepared statements to prepare, can be NULL | ||
909 | * @param flags connection flags | ||
910 | * @return the postgres handle, NULL on error | ||
911 | */ | ||
912 | struct GNUNET_PQ_Context * | ||
913 | GNUNET_PQ_connect_with_cfg2 (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
914 | const char *section, | ||
915 | const char *load_path_suffix, | ||
916 | const struct GNUNET_PQ_ExecuteStatement *es, | ||
917 | const struct GNUNET_PQ_PreparedStatement *ps, | ||
918 | enum GNUNET_PQ_Options flags); | ||
919 | |||
920 | |||
921 | /** | ||
838 | * Reinitialize the database @a db if the connection is down. | 922 | * Reinitialize the database @a db if the connection is down. |
839 | * | 923 | * |
840 | * @param db database connection to reinitialize | 924 | * @param db database connection to reinitialize |
diff --git a/src/pq/pq.h b/src/pq/pq.h index d10931d99..354d85a9f 100644 --- a/src/pq/pq.h +++ b/src/pq/pq.h | |||
@@ -73,6 +73,11 @@ struct GNUNET_PQ_Context | |||
73 | * File descriptor wrapper for @e event_task. | 73 | * File descriptor wrapper for @e event_task. |
74 | */ | 74 | */ |
75 | struct GNUNET_NETWORK_Handle *rfd; | 75 | struct GNUNET_NETWORK_Handle *rfd; |
76 | |||
77 | /** | ||
78 | * Flags controlling the connection. | ||
79 | */ | ||
80 | enum GNUNET_PQ_Options flags; | ||
76 | }; | 81 | }; |
77 | 82 | ||
78 | 83 | ||
diff --git a/src/pq/pq_connect.c b/src/pq/pq_connect.c index a2dce3fb0..a63d5f14e 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, 2019, 2020 GNUnet e.V. | 3 | Copyright (C) 2017, 2019, 2020, 2021 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 |
@@ -70,6 +70,21 @@ GNUNET_PQ_connect (const char *config_str, | |||
70 | const struct GNUNET_PQ_ExecuteStatement *es, | 70 | const struct GNUNET_PQ_ExecuteStatement *es, |
71 | const struct GNUNET_PQ_PreparedStatement *ps) | 71 | const struct GNUNET_PQ_PreparedStatement *ps) |
72 | { | 72 | { |
73 | return GNUNET_PQ_connect2 (config_str, | ||
74 | load_path, | ||
75 | es, | ||
76 | ps, | ||
77 | GNUNET_PQ_FLAG_NONE); | ||
78 | } | ||
79 | |||
80 | |||
81 | struct GNUNET_PQ_Context * | ||
82 | GNUNET_PQ_connect2 (const char *config_str, | ||
83 | const char *load_path, | ||
84 | const struct GNUNET_PQ_ExecuteStatement *es, | ||
85 | const struct GNUNET_PQ_PreparedStatement *ps, | ||
86 | enum GNUNET_PQ_Options flags) | ||
87 | { | ||
73 | struct GNUNET_PQ_Context *db; | 88 | struct GNUNET_PQ_Context *db; |
74 | unsigned int elen = 0; | 89 | unsigned int elen = 0; |
75 | unsigned int plen = 0; | 90 | unsigned int plen = 0; |
@@ -82,6 +97,7 @@ GNUNET_PQ_connect (const char *config_str, | |||
82 | plen++; | 97 | plen++; |
83 | 98 | ||
84 | db = GNUNET_new (struct GNUNET_PQ_Context); | 99 | db = GNUNET_new (struct GNUNET_PQ_Context); |
100 | db->flags = flags; | ||
85 | db->config_str = GNUNET_strdup (config_str); | 101 | db->config_str = GNUNET_strdup (config_str); |
86 | if (NULL != load_path) | 102 | if (NULL != load_path) |
87 | db->load_path = GNUNET_strdup (load_path); | 103 | db->load_path = GNUNET_strdup (load_path); |
@@ -200,68 +216,72 @@ GNUNET_PQ_run_sql (struct GNUNET_PQ_Context *db, | |||
200 | load_path); | 216 | load_path); |
201 | for (unsigned int i = 1; i<10000; i++) | 217 | for (unsigned int i = 1; i<10000; i++) |
202 | { | 218 | { |
219 | char patch_name[slen]; | ||
220 | char buf[slen]; | ||
203 | enum GNUNET_DB_QueryStatus qs; | 221 | enum GNUNET_DB_QueryStatus qs; |
204 | { | 222 | |
205 | char buf[slen]; | 223 | /* First, check patch actually exists */ |
206 | 224 | GNUNET_snprintf (buf, | |
207 | /* First, check patch actually exists */ | 225 | sizeof (buf), |
208 | GNUNET_snprintf (buf, | 226 | "%s%04u.sql", |
209 | sizeof (buf), | 227 | load_path, |
210 | "%s%04u.sql", | 228 | i); |
211 | load_path, | 229 | if (GNUNET_YES != |
212 | i); | 230 | GNUNET_DISK_file_test (buf)) |
213 | if (GNUNET_YES != | 231 | return GNUNET_OK; /* We are done */ |
214 | GNUNET_DISK_file_test (buf)) | ||
215 | return GNUNET_OK; /* We are done */ | ||
216 | } | ||
217 | 232 | ||
218 | /* Second, check with DB versioning schema if this patch was already applied, | 233 | /* Second, check with DB versioning schema if this patch was already applied, |
219 | if so, skip it. */ | 234 | if so, skip it. */ |
235 | GNUNET_snprintf (patch_name, | ||
236 | sizeof (patch_name), | ||
237 | "%s%04u", | ||
238 | load_path_suffix, | ||
239 | i); | ||
220 | { | 240 | { |
221 | char patch_name[slen]; | 241 | char *applied_by; |
222 | 242 | struct GNUNET_PQ_QueryParam params[] = { | |
223 | GNUNET_snprintf (patch_name, | 243 | GNUNET_PQ_query_param_string (patch_name), |
224 | sizeof (patch_name), | 244 | GNUNET_PQ_query_param_end |
225 | "%s%04u", | 245 | }; |
226 | load_path_suffix, | 246 | struct GNUNET_PQ_ResultSpec rs[] = { |
227 | i); | 247 | GNUNET_PQ_result_spec_string ("applied_by", |
248 | &applied_by), | ||
249 | GNUNET_PQ_result_spec_end | ||
250 | }; | ||
251 | |||
252 | qs = GNUNET_PQ_eval_prepared_singleton_select (db, | ||
253 | "gnunet_pq_check_patch", | ||
254 | params, | ||
255 | rs); | ||
256 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) | ||
257 | { | ||
258 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
259 | "Database version %s already applied by %s, skipping\n", | ||
260 | patch_name, | ||
261 | applied_by); | ||
262 | GNUNET_PQ_cleanup_result (rs); | ||
263 | } | ||
264 | if (GNUNET_DB_STATUS_HARD_ERROR == qs) | ||
228 | { | 265 | { |
229 | char *applied_by; | 266 | GNUNET_break (0); |
230 | struct GNUNET_PQ_QueryParam params[] = { | 267 | return GNUNET_SYSERR; |
231 | GNUNET_PQ_query_param_string (patch_name), | ||
232 | GNUNET_PQ_query_param_end | ||
233 | }; | ||
234 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
235 | GNUNET_PQ_result_spec_string ("applied_by", | ||
236 | &applied_by), | ||
237 | GNUNET_PQ_result_spec_end | ||
238 | }; | ||
239 | |||
240 | qs = GNUNET_PQ_eval_prepared_singleton_select (db, | ||
241 | "gnunet_pq_check_patch", | ||
242 | params, | ||
243 | rs); | ||
244 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) | ||
245 | { | ||
246 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
247 | "Database version %s already applied by %s, skipping\n", | ||
248 | patch_name, | ||
249 | applied_by); | ||
250 | GNUNET_PQ_cleanup_result (rs); | ||
251 | } | ||
252 | if (GNUNET_DB_STATUS_HARD_ERROR == qs) | ||
253 | { | ||
254 | GNUNET_break (0); | ||
255 | return GNUNET_SYSERR; | ||
256 | } | ||
257 | } | 268 | } |
258 | } | 269 | } |
259 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) | 270 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) |
260 | continue; /* patch already applied, skip it */ | 271 | continue; /* patch already applied, skip it */ |
261 | 272 | ||
262 | /* patch not yet applied, run it! */ | 273 | if (0 != (GNUNET_PQ_FLAG_CHECK_CURRENT & db->flags)) |
263 | { | 274 | { |
264 | int ret; | 275 | /* We are only checking, found unapplied patch, bad! */ |
276 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
277 | "Database outdated, patch %s missing. Aborting!\n", | ||
278 | patch_name); | ||
279 | return GNUNET_SYSERR; | ||
280 | } | ||
281 | else | ||
282 | { | ||
283 | /* patch not yet applied, run it! */ | ||
284 | enum GNUNET_GenericReturnValue ret; | ||
265 | 285 | ||
266 | ret = apply_patch (db, | 286 | ret = apply_patch (db, |
267 | load_path, | 287 | load_path, |
@@ -334,9 +354,17 @@ GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db) | |||
334 | NULL); | 354 | NULL); |
335 | if (PGRES_COMMAND_OK != PQresultStatus (res)) | 355 | if (PGRES_COMMAND_OK != PQresultStatus (res)) |
336 | { | 356 | { |
337 | int ret; | 357 | enum GNUNET_GenericReturnValue ret; |
338 | 358 | ||
339 | PQclear (res); | 359 | PQclear (res); |
360 | if (0 != (db->flags & GNUNET_PQ_FLAG_DROP)) | ||
361 | { | ||
362 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
363 | "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet. Not attempting drop!\n"); | ||
364 | PQfinish (db->conn); | ||
365 | db->conn = NULL; | ||
366 | return; | ||
367 | } | ||
340 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 368 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, |
341 | "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet, loading patch level 0000!\n"); | 369 | "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet, loading patch level 0000!\n"); |
342 | ret = apply_patch (db, | 370 | ret = apply_patch (db, |
@@ -424,6 +452,23 @@ GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, | |||
424 | const struct GNUNET_PQ_ExecuteStatement *es, | 452 | const struct GNUNET_PQ_ExecuteStatement *es, |
425 | const struct GNUNET_PQ_PreparedStatement *ps) | 453 | const struct GNUNET_PQ_PreparedStatement *ps) |
426 | { | 454 | { |
455 | return GNUNET_PQ_connect_with_cfg2 (cfg, | ||
456 | section, | ||
457 | load_path_suffix, | ||
458 | es, | ||
459 | ps, | ||
460 | GNUNET_PQ_FLAG_NONE); | ||
461 | } | ||
462 | |||
463 | |||
464 | struct GNUNET_PQ_Context * | ||
465 | GNUNET_PQ_connect_with_cfg2 (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
466 | const char *section, | ||
467 | const char *load_path_suffix, | ||
468 | const struct GNUNET_PQ_ExecuteStatement *es, | ||
469 | const struct GNUNET_PQ_PreparedStatement *ps, | ||
470 | enum GNUNET_PQ_Options flags) | ||
471 | { | ||
427 | struct GNUNET_PQ_Context *db; | 472 | struct GNUNET_PQ_Context *db; |
428 | char *conninfo; | 473 | char *conninfo; |
429 | char *load_path; | 474 | char *load_path; |
@@ -447,10 +492,11 @@ GNUNET_PQ_connect_with_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, | |||
447 | "%s%s", | 492 | "%s%s", |
448 | sp, | 493 | sp, |
449 | load_path_suffix); | 494 | load_path_suffix); |
450 | db = GNUNET_PQ_connect (conninfo == NULL ? "" : conninfo, | 495 | db = GNUNET_PQ_connect2 (conninfo == NULL ? "" : conninfo, |
451 | load_path, | 496 | load_path, |
452 | es, | 497 | es, |
453 | ps); | 498 | ps, |
499 | flags); | ||
454 | GNUNET_free (load_path); | 500 | GNUNET_free (load_path); |
455 | GNUNET_free (sp); | 501 | GNUNET_free (sp); |
456 | GNUNET_free (conninfo); | 502 | GNUNET_free (conninfo); |
diff --git a/src/pq/pq_exec.c b/src/pq/pq_exec.c index fd4feae53..464fff4b4 100644 --- a/src/pq/pq_exec.c +++ b/src/pq/pq_exec.c | |||
@@ -72,7 +72,7 @@ GNUNET_PQ_make_try_execute (const char *sql) | |||
72 | * @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) |
73 | * #GNUNET_SYSERR on error | 73 | * #GNUNET_SYSERR on error |
74 | */ | 74 | */ |
75 | int | 75 | enum GNUNET_GenericReturnValue |
76 | GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db, | 76 | GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db, |
77 | const struct GNUNET_PQ_ExecuteStatement *es) | 77 | const struct GNUNET_PQ_ExecuteStatement *es) |
78 | { | 78 | { |