diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-02-09 17:31:41 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-02-09 17:31:41 +0100 |
commit | 99d70615e37294d4f964992c6be0495e95777a27 (patch) | |
tree | 6d51bf3dd75bf299324cf93bebb587ab6eb0d468 /src/pq | |
parent | 8f375b3ea7d53dab21a74b6e08b378bcaab69187 (diff) | |
download | gnunet-99d70615e37294d4f964992c6be0495e95777a27.tar.gz gnunet-99d70615e37294d4f964992c6be0495e95777a27.zip |
use versioning schema to only load database schema patches if not yet covered according to versioning table
Diffstat (limited to 'src/pq')
-rw-r--r-- | src/pq/pq_connect.c | 251 |
1 files changed, 198 insertions, 53 deletions
diff --git a/src/pq/pq_connect.c b/src/pq/pq_connect.c index 6875e9866..e261ce19e 100644 --- a/src/pq/pq_connect.c +++ b/src/pq/pq_connect.c | |||
@@ -135,6 +135,71 @@ GNUNET_PQ_connect (const char *config_str, | |||
135 | 135 | ||
136 | 136 | ||
137 | /** | 137 | /** |
138 | * Apply patch number @a from path @a load_path. | ||
139 | * | ||
140 | * @param db database context to use | ||
141 | * @param load_path where to find the SQL code to run | ||
142 | * @param i patch number to append to the @a load_path | ||
143 | * @return #GNUNET_OK on success, #GNUNET_NO if patch @a i does not exist, #GNUNET_SYSERR on error | ||
144 | */ | ||
145 | static int | ||
146 | apply_patch (struct GNUNET_PQ_Context *db, | ||
147 | const char *load_path, | ||
148 | unsigned int i) | ||
149 | { | ||
150 | size_t slen = strlen (load_path) + 10; | ||
151 | |||
152 | char buf[slen]; | ||
153 | struct GNUNET_OS_Process *psql; | ||
154 | enum GNUNET_OS_ProcessStatusType type; | ||
155 | unsigned long code; | ||
156 | |||
157 | GNUNET_snprintf (buf, | ||
158 | sizeof (buf), | ||
159 | "%s%04u.sql", | ||
160 | load_path, | ||
161 | i); | ||
162 | if (GNUNET_YES != | ||
163 | GNUNET_DISK_file_test (buf)) | ||
164 | return GNUNET_NO; /* We are done */ | ||
165 | psql = GNUNET_OS_start_process (GNUNET_NO, | ||
166 | GNUNET_OS_INHERIT_STD_NONE, | ||
167 | NULL, | ||
168 | NULL, | ||
169 | NULL, | ||
170 | "psql", | ||
171 | "psql", | ||
172 | db->config_str, | ||
173 | "-f", | ||
174 | buf, | ||
175 | "-q", | ||
176 | NULL); | ||
177 | if (NULL == psql) | ||
178 | { | ||
179 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
180 | "exec", | ||
181 | "psql"); | ||
182 | return GNUNET_SYSERR; | ||
183 | } | ||
184 | GNUNET_assert (GNUNET_OK == | ||
185 | GNUNET_OS_process_wait_status (psql, | ||
186 | &type, | ||
187 | &code)); | ||
188 | GNUNET_OS_process_destroy (psql); | ||
189 | if ( (GNUNET_OS_PROCESS_EXITED != type) || | ||
190 | (0 != code) ) | ||
191 | { | ||
192 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
193 | "Could not run PSQL on file %s: %d", | ||
194 | buf, | ||
195 | (int) code); | ||
196 | return GNUNET_SYSERR; | ||
197 | } | ||
198 | return GNUNET_OK; | ||
199 | } | ||
200 | |||
201 | |||
202 | /** | ||
138 | * Within the @a db context, run all the SQL files | 203 | * Within the @a db context, run all the SQL files |
139 | * from the @a load_path from 0000-9999.sql (as long | 204 | * from the @a load_path from 0000-9999.sql (as long |
140 | * as the files exist contiguously). | 205 | * as the files exist contiguously). |
@@ -147,55 +212,75 @@ int | |||
147 | GNUNET_PQ_run_sql (struct GNUNET_PQ_Context *db, | 212 | GNUNET_PQ_run_sql (struct GNUNET_PQ_Context *db, |
148 | const char *load_path) | 213 | const char *load_path) |
149 | { | 214 | { |
150 | size_t slen = strlen (load_path) + 10; | 215 | const char *load_path_suffix; |
216 | |||
151 | 217 | ||
152 | for (unsigned int i = 0; i<10000; i++) | 218 | load_path_suffix = strrchr (load_path, '/'); |
219 | if (NULL == load_path_suffix) | ||
220 | { | ||
221 | GNUNET_break (0); | ||
222 | return GNUNET_SYSERR; | ||
223 | } | ||
224 | load_path_suffix++; /* skip '/' */ | ||
225 | for (unsigned int i = 1; i<10000; i++) | ||
153 | { | 226 | { |
154 | char buf[slen]; | 227 | enum GNUNET_DB_QueryStatus qs; |
155 | struct GNUNET_OS_Process *psql; | 228 | |
156 | enum GNUNET_OS_ProcessStatusType type; | 229 | /* First check with DB versioning schema if this patch was already applied, |
157 | unsigned long code; | 230 | if so, skip it. */ |
158 | |||
159 | GNUNET_snprintf (buf, | ||
160 | sizeof (buf), | ||
161 | "%s%04u.sql", | ||
162 | load_path, | ||
163 | i); | ||
164 | if (GNUNET_YES != | ||
165 | GNUNET_DISK_file_test (buf)) | ||
166 | break; /* We are done */ | ||
167 | psql = GNUNET_OS_start_process (GNUNET_NO, | ||
168 | GNUNET_OS_INHERIT_STD_NONE, | ||
169 | NULL, | ||
170 | NULL, | ||
171 | NULL, | ||
172 | "psql", | ||
173 | "psql", | ||
174 | db->config_str, | ||
175 | "-f", | ||
176 | buf, | ||
177 | "-q", | ||
178 | NULL); | ||
179 | if (NULL == psql) | ||
180 | { | 231 | { |
181 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | 232 | char *patch_name; |
182 | "exec", | 233 | |
183 | "psql"); | 234 | GNUNET_asprintf (&patch_name, |
184 | return GNUNET_SYSERR; | 235 | "%s%04u", |
236 | load_path_suffix, | ||
237 | i); | ||
238 | { | ||
239 | char *applied_by; | ||
240 | struct GNUNET_PQ_QueryParam params[] = { | ||
241 | GNUNET_PQ_query_param_string (patch_name), | ||
242 | GNUNET_PQ_query_param_end | ||
243 | }; | ||
244 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
245 | GNUNET_PQ_result_spec_string ("applied_by", | ||
246 | &applied_by), | ||
247 | GNUNET_PQ_result_spec_end | ||
248 | }; | ||
249 | |||
250 | qs = GNUNET_PQ_eval_prepared_singleton_select (db, | ||
251 | "gnunet_pq_check_patch", | ||
252 | params, | ||
253 | rs); | ||
254 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) | ||
255 | { | ||
256 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
257 | "Database version %s already applied by %s, skipping\n", | ||
258 | patch_name, | ||
259 | applied_by); | ||
260 | GNUNET_PQ_cleanup_result (rs); | ||
261 | } | ||
262 | if (GNUNET_DB_STATUS_HARD_ERROR == qs) | ||
263 | { | ||
264 | GNUNET_break (0); | ||
265 | return GNUNET_SYSERR; | ||
266 | } | ||
267 | } | ||
268 | GNUNET_free (patch_name); | ||
185 | } | 269 | } |
186 | GNUNET_assert (GNUNET_OK == | 270 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) |
187 | GNUNET_OS_process_wait_status (psql, | 271 | continue; /* patch already applied, skip it */ |
188 | &type, | 272 | |
189 | &code)); | 273 | /* patch not yet applied, run it! */ |
190 | GNUNET_OS_process_destroy (psql); | ||
191 | if ( (GNUNET_OS_PROCESS_EXITED != type) || | ||
192 | (0 != code) ) | ||
193 | { | 274 | { |
194 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 275 | int ret; |
195 | "Could not run PSQL on file %s: %d", | 276 | |
196 | buf, | 277 | ret = apply_patch (db, |
197 | (int) code); | 278 | load_path, |
198 | return GNUNET_SYSERR; | 279 | i); |
280 | if (GNUNET_NO == ret) | ||
281 | break; | ||
282 | if (GNUNET_SYSERR == ret) | ||
283 | return GNUNET_SYSERR; | ||
199 | } | 284 | } |
200 | } | 285 | } |
201 | return GNUNET_OK; | 286 | return GNUNET_OK; |
@@ -227,8 +312,8 @@ GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db) | |||
227 | if (NULL != db->conn) | 312 | if (NULL != db->conn) |
228 | PQfinish (db->conn); | 313 | PQfinish (db->conn); |
229 | db->conn = PQconnectdb (db->config_str); | 314 | db->conn = PQconnectdb (db->config_str); |
230 | if ((NULL == db->conn) || | 315 | if ( (NULL == db->conn) || |
231 | (CONNECTION_OK != PQstatus (db->conn))) | 316 | (CONNECTION_OK != PQstatus (db->conn)) ) |
232 | { | 317 | { |
233 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, | 318 | GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, |
234 | "pq", | 319 | "pq", |
@@ -250,14 +335,74 @@ GNUNET_PQ_reconnect (struct GNUNET_PQ_Context *db) | |||
250 | PQsetNoticeProcessor (db->conn, | 335 | PQsetNoticeProcessor (db->conn, |
251 | &pq_notice_processor_cb, | 336 | &pq_notice_processor_cb, |
252 | db); | 337 | db); |
253 | if ( (NULL != db->load_path) && | 338 | if (NULL != db->load_path) |
254 | (GNUNET_OK != | ||
255 | GNUNET_PQ_run_sql (db, | ||
256 | db->load_path)) ) | ||
257 | { | 339 | { |
258 | PQfinish (db->conn); | 340 | PGresult *res; |
259 | db->conn = NULL; | 341 | |
260 | return; | 342 | res = PQprepare (db->conn, |
343 | "gnunet_pq_check_patch", | ||
344 | "SELECT" | ||
345 | " applied_by" | ||
346 | " FROM _v.patches" | ||
347 | " WHERE patch_name = $1" | ||
348 | " LIMIT 1", | ||
349 | 1, | ||
350 | NULL); | ||
351 | if (PGRES_COMMAND_OK != PQresultStatus (res)) | ||
352 | { | ||
353 | int ret; | ||
354 | |||
355 | PQclear (res); | ||
356 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
357 | "Failed to prepare statement to check patch level. Likely versioning schema does not exist yet, loading patch level 0000!\n"); | ||
358 | ret = apply_patch (db, | ||
359 | db->load_path, | ||
360 | 0); | ||
361 | if (GNUNET_NO == ret) | ||
362 | { | ||
363 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
364 | "Failed to find SQL file to load database versioning logic\n"); | ||
365 | PQfinish (db->conn); | ||
366 | db->conn = NULL; | ||
367 | return; | ||
368 | } | ||
369 | if (GNUNET_SYSERR == ret) | ||
370 | { | ||
371 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
372 | "Failed to run SQL logic to setup database versioning logic\n"); | ||
373 | PQfinish (db->conn); | ||
374 | db->conn = NULL; | ||
375 | return; | ||
376 | } | ||
377 | /* try again to prepare our statement! */ | ||
378 | res = PQprepare (db->conn, | ||
379 | "gnunet_pq_check_patch", | ||
380 | "SELECT" | ||
381 | " applied_by" | ||
382 | " FROM _v.patches" | ||
383 | " WHERE patch_name = $1" | ||
384 | " LIMIT 1", | ||
385 | 1, | ||
386 | NULL); | ||
387 | if (PGRES_COMMAND_OK != PQresultStatus (res)) | ||
388 | { | ||
389 | GNUNET_break (0); | ||
390 | PQclear (res); | ||
391 | PQfinish (db->conn); | ||
392 | db->conn = NULL; | ||
393 | return; | ||
394 | } | ||
395 | } | ||
396 | PQclear (res); | ||
397 | |||
398 | if (GNUNET_OK != | ||
399 | GNUNET_PQ_run_sql (db, | ||
400 | db->load_path)) | ||
401 | { | ||
402 | PQfinish (db->conn); | ||
403 | db->conn = NULL; | ||
404 | return; | ||
405 | } | ||
261 | } | 406 | } |
262 | if ( (NULL != db->es) && | 407 | if ( (NULL != db->es) && |
263 | (GNUNET_OK != | 408 | (GNUNET_OK != |