aboutsummaryrefslogtreecommitdiff
path: root/src/pq
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-02-09 17:31:41 +0100
committerChristian Grothoff <christian@grothoff.org>2020-02-09 17:31:41 +0100
commit99d70615e37294d4f964992c6be0495e95777a27 (patch)
tree6d51bf3dd75bf299324cf93bebb587ab6eb0d468 /src/pq
parent8f375b3ea7d53dab21a74b6e08b378bcaab69187 (diff)
downloadgnunet-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.c251
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 */
145static int
146apply_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
147GNUNET_PQ_run_sql (struct GNUNET_PQ_Context *db, 212GNUNET_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 !=