aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-09-23 22:52:44 +0200
committerChristian Grothoff <christian@grothoff.org>2021-09-23 22:52:44 +0200
commit1aa0d5f3421d8598f12005ea1138c9eb24ddfd2c (patch)
treea3f1ac44d4588b1c37daa804f3c4c4b9b839456b
parent8f18cbcaf6025d40fa1d400f5a4e702ad957809a (diff)
downloadgnunet-1aa0d5f3421d8598f12005ea1138c9eb24ddfd2c.tar.gz
gnunet-1aa0d5f3421d8598f12005ea1138c9eb24ddfd2c.zip
PQ: implementing #7014: support testing database version is current
-rw-r--r--src/include/gnunet_pq_lib.h102
-rw-r--r--src/pq/pq.h5
-rw-r--r--src/pq/pq_connect.c158
-rw-r--r--src/pq/pq_exec.c2
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 */
472struct GNUNET_PQ_ResultSpec 472struct GNUNET_PQ_ResultSpec
473GNUNET_PQ_result_spec_uint16 (const char *name, uint16_t *u16); 473GNUNET_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 */
483struct GNUNET_PQ_ResultSpec 484struct GNUNET_PQ_ResultSpec
484GNUNET_PQ_result_spec_uint32 (const char *name, uint32_t *u32); 485GNUNET_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 */
494struct GNUNET_PQ_ResultSpec 496struct GNUNET_PQ_ResultSpec
495GNUNET_PQ_result_spec_uint64 (const char *name, uint64_t *u64); 497GNUNET_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 */
643enum GNUNET_DB_QueryStatus 646enum GNUNET_DB_QueryStatus
644GNUNET_PQ_eval_prepared_singleton_select (struct GNUNET_PQ_Context *db, 647GNUNET_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 */
775int 778enum GNUNET_GenericReturnValue
776GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db, 779GNUNET_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 */
789enum 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 */
861struct GNUNET_PQ_Context *
862GNUNET_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 */
912struct GNUNET_PQ_Context *
913GNUNET_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
81struct GNUNET_PQ_Context *
82GNUNET_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
464struct GNUNET_PQ_Context *
465GNUNET_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 */
75int 75enum GNUNET_GenericReturnValue
76GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db, 76GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db,
77 const struct GNUNET_PQ_ExecuteStatement *es) 77 const struct GNUNET_PQ_ExecuteStatement *es)
78{ 78{