1 /*****************************************************************************\
2 * mysql_common.c - common functions for the mysql storage plugin.
3 *****************************************************************************
4 * Copyright (C) 2004-2007 The Regents of the University of California.
5 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
6 * Written by Danny Auble <da@llnl.gov>
7 *
8 * This file is part of Slurm, a resource management program.
9 * For details, see <https://slurm.schedmd.com/>.
10 * Please also read the included file: DISCLAIMER.
11 *
12 * Slurm is free software; you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 2 of the License, or (at your option)
15 * any later version.
16 *
17 * In addition, as a special exception, the copyright holders give permission
18 * to link the code of portions of this program with the OpenSSL library under
19 * certain conditions as described in each individual source file, and
20 * distribute linked combinations including the two. You must obey the GNU
21 * General Public License in all respects for all of the code used other than
22 * OpenSSL. If you modify file(s) with this exception, you may extend this
23 * exception to your version of the file(s), but you are not obligated to do
24 * so. If you do not wish to do so, delete this exception statement from your
25 * version. If you delete this exception statement from all source files in
26 * the program, then also delete it here.
27 *
28 * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
29 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
30 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
31 * details.
32 *
33 * You should have received a copy of the GNU General Public License along
34 * with Slurm; if not, write to the Free Software Foundation, Inc.,
35 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
36 *
37 * This file is patterned after jobcomp_linux.c, written by Morris Jette and
38 * Copyright (C) 2002 The Regents of the University of California.
39 \*****************************************************************************/
40
41 #include "config.h"
42
43 #include "mysql_common.h"
44 #include "src/common/log.h"
45 #include "src/common/xstring.h"
46 #include "src/common/xmalloc.h"
47 #include "src/common/timers.h"
48 #include "src/common/slurm_protocol_api.h"
49 #include "src/common/read_config.h"
50
51 #define MAX_DEADLOCK_ATTEMPTS 10
52
53 static char *table_defs_table = "table_defs_table";
54
55 typedef struct {
56 char *name;
57 char *columns;
58 } db_key_t;
59
_destroy_db_key(void * arg)60 static void _destroy_db_key(void *arg)
61 {
62 db_key_t *db_key = (db_key_t *)arg;
63
64 if (db_key) {
65 xfree(db_key->name);
66 xfree(db_key->columns);
67 xfree(db_key);
68 }
69 }
70
71 /* NOTE: Ensure that mysql_conn->lock is set on function entry */
_clear_results(MYSQL * db_conn)72 static int _clear_results(MYSQL *db_conn)
73 {
74 MYSQL_RES *result = NULL;
75 int rc = 0;
76
77 do {
78 /* did current statement return data? */
79 if ((result = mysql_store_result(db_conn)))
80 mysql_free_result(result);
81
82 /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */
83 if ((rc = mysql_next_result(db_conn)) > 0)
84 error("Could not execute statement %d %s",
85 mysql_errno(db_conn),
86 mysql_error(db_conn));
87 } while (rc == 0);
88
89 if (rc > 0) {
90 errno = rc;
91 return SLURM_ERROR;
92 }
93 return SLURM_SUCCESS;
94 }
95
96 /* NOTE: Ensure that mysql_conn->lock is set on function entry */
_get_first_result(MYSQL * db_conn)97 static MYSQL_RES *_get_first_result(MYSQL *db_conn)
98 {
99 MYSQL_RES *result = NULL;
100 int rc = 0;
101 do {
102 /* did current statement return data? */
103 if ((result = mysql_store_result(db_conn)))
104 return result;
105
106 /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */
107 if ((rc = mysql_next_result(db_conn)) > 0)
108 debug3("error: Could not execute statement %d", rc);
109
110 } while (rc == 0);
111
112 return NULL;
113 }
114
115 /* NOTE: Ensure that mysql_conn->lock is set on function entry */
_get_last_result(MYSQL * db_conn)116 static MYSQL_RES *_get_last_result(MYSQL *db_conn)
117 {
118 MYSQL_RES *result = NULL;
119 MYSQL_RES *last_result = NULL;
120 int rc = 0;
121 do {
122 /* did current statement return data? */
123 if ((result = mysql_store_result(db_conn))) {
124 if (last_result)
125 mysql_free_result(last_result);
126 last_result = result;
127 }
128 /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */
129 if ((rc = mysql_next_result(db_conn)) > 0)
130 debug3("error: Could not execute statement %d", rc);
131 } while (rc == 0);
132
133 return last_result;
134 }
135
136 /* NOTE: Ensure that mysql_conn->lock is set on function entry */
_mysql_query_internal(MYSQL * db_conn,char * query)137 static int _mysql_query_internal(MYSQL *db_conn, char *query)
138 {
139 int rc = SLURM_SUCCESS;
140 int deadlock_attempt = 0;
141
142 try_again:
143 if (!db_conn)
144 fatal("You haven't inited this storage yet.");
145
146 /* clear out the old results so we don't get a 2014 error */
147 _clear_results(db_conn);
148 if (mysql_query(db_conn, query)) {
149 const char *err_str = mysql_error(db_conn);
150 errno = mysql_errno(db_conn);
151 if (errno == ER_NO_SUCH_TABLE) {
152 debug4("This could happen often and is expected.\n"
153 "mysql_query failed: %d %s\n%s",
154 errno, err_str, query);
155 errno = 0;
156 goto end_it;
157 }
158 error("mysql_query failed: %d %s\n%s", errno, err_str, query);
159 if (errno == ER_LOCK_DEADLOCK) {
160 /*
161 * Mysql detected a deadlock and we should retry
162 * a few times since this is mainly a race condition
163 */
164 deadlock_attempt++;
165
166 if (deadlock_attempt < MAX_DEADLOCK_ATTEMPTS) {
167 error("%s: deadlock detected attempt %u/%u: %d %s",
168 __func__, deadlock_attempt,
169 MAX_DEADLOCK_ATTEMPTS, errno, err_str);
170 goto try_again;
171 } else {
172 fatal("%s: unable to resolve deadlock with attempts %u/%u: %d %s\nPlease call 'show engine innodb status;' in MySQL/MariaDB and open a bug report with SchedMD.",
173 __func__, deadlock_attempt,
174 MAX_DEADLOCK_ATTEMPTS, errno, err_str);
175 }
176 } else if (errno == ER_LOCK_WAIT_TIMEOUT) {
177 /* FIXME: If we get ER_LOCK_WAIT_TIMEOUT here we need
178 * to restart the connections, but it appears restarting
179 * the calling program is the only way to handle this.
180 * If anyone in the future figures out a way to handle
181 * this, super. Until then we will need to restart the
182 * calling program if you ever get this error.
183 */
184 fatal("mysql gave ER_LOCK_WAIT_TIMEOUT as an error. "
185 "The only way to fix this is restart the "
186 "calling program");
187 } else if (errno == ER_HOST_IS_BLOCKED) {
188 fatal("MySQL gave ER_HOST_IS_BLOCKED as an error. "
189 "You will need to call 'mysqladmin flush-hosts' "
190 "to regain connectivity.");
191 }
192 rc = SLURM_ERROR;
193 }
194 end_it:
195 /*
196 * Starting in MariaDB 10.2 many of the api commands started
197 * setting errno erroneously.
198 */
199 if (!rc)
200 errno = 0;
201
202 return rc;
203 }
204
205 /* NOTE: Ensure that mysql_conn->lock is NOT set on function entry */
_mysql_make_table_current(mysql_conn_t * mysql_conn,char * table_name,storage_field_t * fields,char * ending)206 static int _mysql_make_table_current(mysql_conn_t *mysql_conn, char *table_name,
207 storage_field_t *fields, char *ending)
208 {
209 char *query = NULL;
210 char *correct_query = NULL;
211 MYSQL_RES *result = NULL;
212 MYSQL_ROW row;
213 int i = 0;
214 List columns = NULL;
215 ListIterator itr = NULL;
216 char *col = NULL;
217 int adding = 0;
218 int run_update = 0;
219 char *primary_key = NULL;
220 char *unique_index = NULL;
221 int old_primary = 0;
222 char *old_index = NULL;
223 char *temp = NULL, *temp2 = NULL;
224 List keys_list = NULL;
225 db_key_t *db_key = NULL;
226
227 DEF_TIMERS;
228
229 /* figure out the unique keys in the table */
230 query = xstrdup_printf("show index from %s where non_unique=0",
231 table_name);
232 if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
233 xfree(query);
234 return SLURM_ERROR;
235 }
236 xfree(query);
237 while ((row = mysql_fetch_row(result))) {
238 // row[2] is the key name
239 if (!xstrcasecmp(row[2], "PRIMARY"))
240 old_primary = 1;
241 else if (!old_index)
242 old_index = xstrdup(row[2]);
243 }
244 mysql_free_result(result);
245
246 /* figure out the non-unique keys in the table */
247 query = xstrdup_printf("show index from %s where non_unique=1",
248 table_name);
249 if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
250 xfree(query);
251 xfree(old_index);
252 return SLURM_ERROR;
253 }
254 xfree(query);
255
256 itr = NULL;
257 keys_list = list_create(_destroy_db_key);
258 while ((row = mysql_fetch_row(result))) {
259 if (!itr)
260 itr = list_iterator_create(keys_list);
261 else
262 list_iterator_reset(itr);
263 while ((db_key = list_next(itr))) {
264 if (!xstrcmp(db_key->name, row[2]))
265 break;
266 }
267
268 if (db_key) {
269 xstrfmtcat(db_key->columns, ", %s", row[4]);
270 } else {
271 db_key = xmalloc(sizeof(db_key_t));
272 db_key->name = xstrdup(row[2]); // name
273 db_key->columns = xstrdup(row[4]); // column name
274 list_append(keys_list, db_key); // don't use list_push
275 }
276 }
277 mysql_free_result(result);
278
279 if (itr) {
280 list_iterator_destroy(itr);
281 itr = NULL;
282 }
283
284 /* figure out the existing columns in the table */
285 query = xstrdup_printf("show columns from %s", table_name);
286 if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) {
287 xfree(query);
288 xfree(old_index);
289 FREE_NULL_LIST(keys_list);
290 return SLURM_ERROR;
291 }
292 xfree(query);
293 columns = list_create(xfree_ptr);
294 while ((row = mysql_fetch_row(result))) {
295 col = xstrdup(row[0]); //Field
296 list_append(columns, col);
297 }
298 mysql_free_result(result);
299
300
301 itr = list_iterator_create(columns);
302 /* In MySQL 5.7.4 we lost the ability to run 'alter ignore'. This was
303 * needed when converting old tables to new schemas. If people convert
304 * in the future from an older version of Slurm that needed the ignore
305 * to work they will have to downgrade mysql to <= 5.7.3 to make things
306 * work correctly or manually edit the database to get things to work.
307 */
308 /*
309 * `query` is compared against the current table_defs_table.definition
310 * and run if they are different. `correct_query` is inserted into the
311 * table, so it must be what future `query` schemas will be.
312 * In other words, `query` transitions the table to the new schema,
313 * `correct_query` represents the new schema
314 */
315 query = xstrdup_printf("alter table %s", table_name);
316 correct_query = xstrdup(query);
317 START_TIMER;
318 while (fields[i].name) {
319 int found = 0;
320
321 list_iterator_reset(itr);
322 while ((col = list_next(itr))) {
323 if (!xstrcmp(col, fields[i].name)) {
324 xstrfmtcat(query, " modify `%s` %s,",
325 fields[i].name,
326 fields[i].options);
327 xstrfmtcat(correct_query, " modify `%s` %s,",
328 fields[i].name,
329 fields[i].options);
330 list_delete_item(itr);
331 found = 1;
332 break;
333 }
334 }
335 if (!found) {
336 if (i) {
337 info("adding column %s after %s in table %s",
338 fields[i].name,
339 fields[i-1].name,
340 table_name);
341 xstrfmtcat(query, " add `%s` %s after %s,",
342 fields[i].name,
343 fields[i].options,
344 fields[i-1].name);
345 xstrfmtcat(correct_query, " modify `%s` %s,",
346 fields[i].name,
347 fields[i].options);
348 } else {
349 info("adding column %s at the beginning "
350 "of table %s",
351 fields[i].name,
352 table_name);
353 xstrfmtcat(query, " add `%s` %s first,",
354 fields[i].name,
355 fields[i].options);
356 xstrfmtcat(correct_query, " modify `%s` %s,",
357 fields[i].name,
358 fields[i].options);
359 }
360 adding = 1;
361 }
362
363 i++;
364 }
365
366 list_iterator_reset(itr);
367 while ((col = list_next(itr))) {
368 adding = 1;
369 info("dropping column %s from table %s", col, table_name);
370 xstrfmtcat(query, " drop %s,", col);
371 }
372
373 list_iterator_destroy(itr);
374 FREE_NULL_LIST(columns);
375
376 if ((temp = strstr(ending, "primary key ("))) {
377 int open = 0, close =0;
378 int end = 0;
379 while (temp[end++]) {
380 if (temp[end] == '(')
381 open++;
382 else if (temp[end] == ')')
383 close++;
384 else
385 continue;
386 if (open == close)
387 break;
388 }
389 if (temp[end]) {
390 end++;
391 primary_key = xstrndup(temp, end);
392 if (old_primary)
393 xstrcat(query, " drop primary key,");
394 xstrcat(correct_query, " drop primary key,");
395 xstrfmtcat(query, " add %s,", primary_key);
396 xstrfmtcat(correct_query, " add %s,", primary_key);
397
398 xfree(primary_key);
399 }
400 }
401
402 if ((temp = strstr(ending, "unique index"))) {
403 int open = 0, close = 0;
404 /* sizeof includes NULL, and end should start 1 back */
405 int end = sizeof("unique index") - 2;
406 char *udex_name = NULL, *name_marker = NULL;
407 while (temp[end++]) {
408 /*
409 * Extracts the index name, which is given explicitly
410 * or is the name of the first field included in the
411 * index.
412 * "unique index indexname (field1, field2)"
413 * "unique index (indexname, field2)"
414 * indexname is started by the first non '(' or ' '
415 * after "unique index"
416 * indexname is terminated by '(' ')' ' ' or ','
417 */
418 if (name_marker) {
419 if (!udex_name && (temp[end] == '(' ||
420 temp[end] == ')' ||
421 temp[end] == ' ' ||
422 temp[end] == ','))
423 udex_name = xstrndup(name_marker,
424 temp + end - name_marker);
425 } else if (temp[end] != '(' && temp[end] != ' ') {
426 name_marker = temp + end;
427 }
428
429 /* find the end of the parenthetical expression */
430 if (temp[end] == '(')
431 open++;
432 else if (temp[end] == ')')
433 close++;
434 else
435 continue;
436 if (open == close)
437 break;
438 }
439 if (temp[end]) {
440 end++;
441 unique_index = xstrndup(temp, end);
442 if (old_index)
443 xstrfmtcat(query, " drop index %s,", old_index);
444 xstrfmtcat(correct_query, " drop index %s,", udex_name);
445 xstrfmtcat(query, " add %s,", unique_index);
446 xstrfmtcat(correct_query, " add %s,", unique_index);
447 xfree(unique_index);
448 }
449 xfree(udex_name);
450 }
451 xfree(old_index);
452
453 temp2 = ending;
454 itr = list_iterator_create(keys_list);
455 while ((temp = strstr(temp2, ", key "))) {
456 int open = 0, close = 0, name_end = 0;
457 int end = 5;
458 char *new_key_name = NULL, *new_key = NULL;
459 while (temp[end++]) {
460 if (!name_end && (temp[end] == ' ')) {
461 name_end = end;
462 continue;
463 } else if (temp[end] == '(') {
464 open++;
465 if (!name_end)
466 name_end = end;
467 } else if (temp[end] == ')')
468 close++;
469 else
470 continue;
471 if (open == close)
472 break;
473 }
474 if (temp[end]) {
475 end++;
476 new_key_name = xstrndup(temp+6, name_end-6);
477 new_key = xstrndup(temp+2, end-2); // skip ', '
478 while ((db_key = list_next(itr))) {
479 if (!xstrcmp(db_key->name, new_key_name)) {
480 list_remove(itr);
481 break;
482 }
483 }
484 list_iterator_reset(itr);
485 if (db_key) {
486 xstrfmtcat(query,
487 " drop key %s,", db_key->name);
488 _destroy_db_key(db_key);
489 } else
490 info("adding %s to table %s",
491 new_key, table_name);
492 xstrfmtcat(correct_query,
493 " drop key %s,", new_key_name);
494
495 xstrfmtcat(query, " add %s,", new_key);
496 xstrfmtcat(correct_query, " add %s,", new_key);
497
498 xfree(new_key);
499 xfree(new_key_name);
500 }
501 temp2 = temp + end;
502 }
503
504 /* flush extra (old) keys */
505 while ((db_key = list_next(itr))) {
506 info("dropping key %s from table %s", db_key->name, table_name);
507 xstrfmtcat(query, " drop key %s,", db_key->name);
508 }
509 list_iterator_destroy(itr);
510
511 FREE_NULL_LIST(keys_list);
512
513 query[strlen(query)-1] = ';';
514 correct_query[strlen(correct_query)-1] = ';';
515 //info("%d query\n%s", __LINE__, query);
516
517 /* see if we have already done this definition */
518 if (!adding) {
519 char *quoted = slurm_add_slash_to_quotes(query);
520 char *query2 = xstrdup_printf("select table_name from "
521 "%s where definition='%s'",
522 table_defs_table, quoted);
523 MYSQL_RES *result = NULL;
524 MYSQL_ROW row;
525
526 xfree(quoted);
527 run_update = 1;
528 if ((result = mysql_db_query_ret(mysql_conn, query2, 0))) {
529 if ((row = mysql_fetch_row(result)))
530 run_update = 0;
531 mysql_free_result(result);
532 }
533 xfree(query2);
534 if (run_update) {
535 run_update = 2;
536 query2 = xstrdup_printf("select table_name from "
537 "%s where table_name='%s'",
538 table_defs_table, table_name);
539 if ((result = mysql_db_query_ret(
540 mysql_conn, query2, 0))) {
541 if ((row = mysql_fetch_row(result)))
542 run_update = 1;
543 mysql_free_result(result);
544 }
545 xfree(query2);
546 }
547 }
548
549 /* if something has changed run the alter line */
550 if (run_update || adding) {
551 time_t now = time(NULL);
552 char *query2 = NULL;
553 char *quoted = NULL;
554
555 if (run_update == 2)
556 debug4("Table %s doesn't exist, adding", table_name);
557 else
558 debug("Table %s has changed. Updating...", table_name);
559 if (mysql_db_query(mysql_conn, query)) {
560 xfree(query);
561 return SLURM_ERROR;
562 }
563 quoted = slurm_add_slash_to_quotes(correct_query);
564 query2 = xstrdup_printf("insert into %s (creation_time, "
565 "mod_time, table_name, definition) "
566 "values (%ld, %ld, '%s', '%s') "
567 "on duplicate key update "
568 "definition='%s', mod_time=%ld;",
569 table_defs_table, now, now,
570 table_name, quoted,
571 quoted, now);
572 xfree(quoted);
573 if (mysql_db_query(mysql_conn, query2)) {
574 xfree(query2);
575 return SLURM_ERROR;
576 }
577 xfree(query2);
578 }
579
580 xfree(query);
581 xfree(correct_query);
582 query = xstrdup_printf("make table current %s", table_name);
583 END_TIMER2(query);
584 xfree(query);
585 return SLURM_SUCCESS;
586 }
587
588 /* NOTE: Ensure that mysql_conn->lock is set on function entry */
_create_db(char * db_name,mysql_db_info_t * db_info)589 static int _create_db(char *db_name, mysql_db_info_t *db_info)
590 {
591 MYSQL *mysql_db = NULL;
592 int rc = SLURM_ERROR;
593
594 MYSQL *db_ptr = NULL;
595 char *db_host = NULL;
596
597 while (rc == SLURM_ERROR) {
598 rc = SLURM_SUCCESS;
599 if (!(mysql_db = mysql_init(mysql_db)))
600 fatal("mysql_init failed: %s", mysql_error(mysql_db));
601
602 db_host = db_info->host;
603 db_ptr = mysql_real_connect(mysql_db,
604 db_host, db_info->user,
605 db_info->pass, NULL,
606 db_info->port, NULL, 0);
607
608 if (!db_ptr && db_info->backup) {
609 info("Connection failed to host = %s "
610 "user = %s port = %u",
611 db_host, db_info->user,
612 db_info->port);
613 db_host = db_info->backup;
614 db_ptr = mysql_real_connect(mysql_db, db_host,
615 db_info->user,
616 db_info->pass, NULL,
617 db_info->port, NULL, 0);
618 }
619
620 if (db_ptr) {
621 char *create_line = NULL;
622 xstrfmtcat(create_line, "create database %s", db_name);
623 if (mysql_query(mysql_db, create_line)) {
624 fatal("mysql_query failed: %d %s\n%s",
625 mysql_errno(mysql_db),
626 mysql_error(mysql_db), create_line);
627 }
628 xfree(create_line);
629 if (mysql_thread_safe())
630 mysql_thread_end();
631 mysql_close(mysql_db);
632 } else {
633 info("Connection failed to host = %s "
634 "user = %s port = %u",
635 db_host, db_info->user,
636 db_info->port);
637 error("mysql_real_connect failed: %d %s",
638 mysql_errno(mysql_db),
639 mysql_error(mysql_db));
640 rc = SLURM_ERROR;
641 }
642 if (rc == SLURM_ERROR)
643 sleep(3);
644 }
645 return rc;
646 }
647
create_mysql_conn(int conn_num,bool rollback,char * cluster_name)648 extern mysql_conn_t *create_mysql_conn(int conn_num, bool rollback,
649 char *cluster_name)
650 {
651 mysql_conn_t *mysql_conn = xmalloc(sizeof(mysql_conn_t));
652
653 mysql_conn->rollback = rollback;
654 mysql_conn->conn = conn_num;
655 mysql_conn->cluster_name = xstrdup(cluster_name);
656 slurm_mutex_init(&mysql_conn->lock);
657 mysql_conn->update_list = list_create(slurmdb_destroy_update_object);
658
659 return mysql_conn;
660 }
661
destroy_mysql_conn(mysql_conn_t * mysql_conn)662 extern int destroy_mysql_conn(mysql_conn_t *mysql_conn)
663 {
664 if (mysql_conn) {
665 mysql_db_close_db_connection(mysql_conn);
666 xfree(mysql_conn->pre_commit_query);
667 xfree(mysql_conn->cluster_name);
668 slurm_mutex_destroy(&mysql_conn->lock);
669 FREE_NULL_LIST(mysql_conn->update_list);
670 xfree(mysql_conn);
671 }
672
673 return SLURM_SUCCESS;
674 }
675
create_mysql_db_info(slurm_mysql_plugin_type_t type)676 extern mysql_db_info_t *create_mysql_db_info(slurm_mysql_plugin_type_t type)
677 {
678 mysql_db_info_t *db_info = xmalloc(sizeof(mysql_db_info_t));
679
680 switch (type) {
681 case SLURM_MYSQL_PLUGIN_AS:
682 db_info->port = slurm_get_accounting_storage_port();
683 if (!db_info->port) {
684 db_info->port = DEFAULT_MYSQL_PORT;
685 slurm_set_accounting_storage_port(db_info->port);
686 }
687 db_info->host = slurm_get_accounting_storage_host();
688 db_info->backup = slurm_get_accounting_storage_backup_host();
689 db_info->user = slurm_get_accounting_storage_user();
690 db_info->pass = slurm_get_accounting_storage_pass();
691 break;
692 case SLURM_MYSQL_PLUGIN_JC:
693 db_info->port = slurm_get_jobcomp_port();
694 if (!db_info->port) {
695 db_info->port = DEFAULT_MYSQL_PORT;
696 slurm_set_jobcomp_port(db_info->port);
697 }
698 db_info->host = slurm_get_jobcomp_host();
699 db_info->user = slurm_get_jobcomp_user();
700 db_info->pass = slurm_get_jobcomp_pass();
701 break;
702 default:
703 xfree(db_info);
704 fatal("Unknown mysql_db_info %d", type);
705 }
706 return db_info;
707 }
708
destroy_mysql_db_info(mysql_db_info_t * db_info)709 extern int destroy_mysql_db_info(mysql_db_info_t *db_info)
710 {
711 if (db_info) {
712 xfree(db_info->backup);
713 xfree(db_info->host);
714 xfree(db_info->user);
715 xfree(db_info->pass);
716 xfree(db_info);
717 }
718 return SLURM_SUCCESS;
719 }
720
mysql_db_get_db_connection(mysql_conn_t * mysql_conn,char * db_name,mysql_db_info_t * db_info)721 extern int mysql_db_get_db_connection(mysql_conn_t *mysql_conn, char *db_name,
722 mysql_db_info_t *db_info)
723 {
724 int rc = SLURM_SUCCESS;
725 bool storage_init = false;
726 char *db_host = db_info->host;
727 unsigned int my_timeout = 30;
728 #ifdef MYSQL_OPT_RECONNECT
729 my_bool reconnect = 1;
730 #endif
731 xassert(mysql_conn);
732
733 slurm_mutex_lock(&mysql_conn->lock);
734
735 if (!(mysql_conn->db_conn = mysql_init(mysql_conn->db_conn))) {
736 slurm_mutex_unlock(&mysql_conn->lock);
737 fatal("mysql_init failed: %s",
738 mysql_error(mysql_conn->db_conn));
739 }
740
741 /* If this ever changes you will need to alter
742 * src/common/slurmdbd_defs.c function _send_init_msg to
743 * handle a different timeout when polling for the
744 * response.
745 */
746 #ifdef MYSQL_OPT_RECONNECT
747 /* make sure reconnect is on */
748 mysql_options(mysql_conn->db_conn, MYSQL_OPT_RECONNECT,
749 &reconnect);
750 #endif
751 mysql_options(mysql_conn->db_conn, MYSQL_OPT_CONNECT_TIMEOUT,
752 (char *)&my_timeout);
753 while (!storage_init) {
754 debug2("Attempting to connect to %s:%d", db_host,
755 db_info->port);
756 if (!mysql_real_connect(mysql_conn->db_conn, db_host,
757 db_info->user, db_info->pass,
758 db_name, db_info->port, NULL,
759 CLIENT_MULTI_STATEMENTS)) {
760 const char *err_str = NULL;
761 int err = mysql_errno(mysql_conn->db_conn);
762
763 if (err == ER_BAD_DB_ERROR) {
764 debug("Database %s not created. Creating",
765 db_name);
766 rc = _create_db(db_name, db_info);
767 continue;
768 }
769
770 err_str = mysql_error(mysql_conn->db_conn);
771
772 if ((db_host == db_info->host) && db_info->backup) {
773 debug2("mysql_real_connect failed: %d %s",
774 err, err_str);
775 db_host = db_info->backup;
776 continue;
777 }
778
779 error("mysql_real_connect failed: %d %s",
780 err, err_str);
781 rc = ESLURM_DB_CONNECTION;
782 mysql_close(mysql_conn->db_conn);
783 mysql_conn->db_conn = NULL;
784 break;
785 }
786
787 storage_init = true;
788 if (mysql_conn->rollback)
789 mysql_autocommit(mysql_conn->db_conn, 0);
790 rc = _mysql_query_internal(mysql_conn->db_conn,
791 "SET session sql_mode='ANSI_QUOTES,"
792 "NO_ENGINE_SUBSTITUTION';");
793 }
794 slurm_mutex_unlock(&mysql_conn->lock);
795 errno = rc;
796 return rc;
797 }
798
mysql_db_close_db_connection(mysql_conn_t * mysql_conn)799 extern int mysql_db_close_db_connection(mysql_conn_t *mysql_conn)
800 {
801 slurm_mutex_lock(&mysql_conn->lock);
802 if (mysql_conn && mysql_conn->db_conn) {
803 if (mysql_thread_safe())
804 mysql_thread_end();
805 mysql_close(mysql_conn->db_conn);
806 mysql_conn->db_conn = NULL;
807 }
808 slurm_mutex_unlock(&mysql_conn->lock);
809 return SLURM_SUCCESS;
810 }
811
mysql_db_cleanup()812 extern int mysql_db_cleanup()
813 {
814 debug3("starting mysql cleaning up");
815
816 #ifdef mysql_library_end
817 mysql_library_end();
818 #else
819 mysql_server_end();
820 #endif
821 debug3("finished mysql cleaning up");
822 return SLURM_SUCCESS;
823 }
824
mysql_db_query(mysql_conn_t * mysql_conn,char * query)825 extern int mysql_db_query(mysql_conn_t *mysql_conn, char *query)
826 {
827 int rc = SLURM_SUCCESS;
828
829 if (!mysql_conn || !mysql_conn->db_conn) {
830 fatal("You haven't inited this storage yet.");
831 return 0; /* For CLANG false positive */
832 }
833 slurm_mutex_lock(&mysql_conn->lock);
834 rc = _mysql_query_internal(mysql_conn->db_conn, query);
835 slurm_mutex_unlock(&mysql_conn->lock);
836 return rc;
837 }
838
839 /*
840 * Executes a single delete sql query.
841 * Returns the number of deleted rows, <0 for failure.
842 */
mysql_db_delete_affected_rows(mysql_conn_t * mysql_conn,char * query)843 extern int mysql_db_delete_affected_rows(mysql_conn_t *mysql_conn, char *query)
844 {
845 int rc = SLURM_SUCCESS;
846
847 if (!mysql_conn || !mysql_conn->db_conn) {
848 fatal("You haven't inited this storage yet.");
849 return 0; /* For CLANG false positive */
850 }
851 slurm_mutex_lock(&mysql_conn->lock);
852 if (!(rc = _mysql_query_internal(mysql_conn->db_conn, query)))
853 rc = mysql_affected_rows(mysql_conn->db_conn);
854 slurm_mutex_unlock(&mysql_conn->lock);
855 return rc;
856 }
857
mysql_db_ping(mysql_conn_t * mysql_conn)858 extern int mysql_db_ping(mysql_conn_t *mysql_conn)
859 {
860 int rc;
861
862 if (!mysql_conn->db_conn)
863 return -1;
864
865 /* clear out the old results so we don't get a 2014 error */
866 slurm_mutex_lock(&mysql_conn->lock);
867 _clear_results(mysql_conn->db_conn);
868 rc = mysql_ping(mysql_conn->db_conn);
869 /*
870 * Starting in MariaDB 10.2 many of the api commands started
871 * setting errno erroneously.
872 */
873 if (!rc)
874 errno = 0;
875 slurm_mutex_unlock(&mysql_conn->lock);
876 return rc;
877 }
878
mysql_db_commit(mysql_conn_t * mysql_conn)879 extern int mysql_db_commit(mysql_conn_t *mysql_conn)
880 {
881 int rc = SLURM_SUCCESS;
882
883 if (!mysql_conn->db_conn)
884 return SLURM_ERROR;
885
886 slurm_mutex_lock(&mysql_conn->lock);
887 /* clear out the old results so we don't get a 2014 error */
888 _clear_results(mysql_conn->db_conn);
889 if (mysql_commit(mysql_conn->db_conn)) {
890 error("mysql_commit failed: %d %s",
891 mysql_errno(mysql_conn->db_conn),
892 mysql_error(mysql_conn->db_conn));
893 errno = mysql_errno(mysql_conn->db_conn);
894 rc = SLURM_ERROR;
895 }
896 slurm_mutex_unlock(&mysql_conn->lock);
897 return rc;
898 }
899
mysql_db_rollback(mysql_conn_t * mysql_conn)900 extern int mysql_db_rollback(mysql_conn_t *mysql_conn)
901 {
902 int rc = SLURM_SUCCESS;
903
904 if (!mysql_conn->db_conn)
905 return SLURM_ERROR;
906
907 slurm_mutex_lock(&mysql_conn->lock);
908 /* clear out the old results so we don't get a 2014 error */
909 _clear_results(mysql_conn->db_conn);
910 if (mysql_rollback(mysql_conn->db_conn)) {
911 error("mysql_commit failed: %d %s",
912 mysql_errno(mysql_conn->db_conn),
913 mysql_error(mysql_conn->db_conn));
914 errno = mysql_errno(mysql_conn->db_conn);
915 rc = SLURM_ERROR;
916 } else {
917 /*
918 * Starting in MariaDB 10.2 many of the api commands started
919 * setting errno erroneously.
920 */
921 errno = 0;
922 }
923 slurm_mutex_unlock(&mysql_conn->lock);
924 return rc;
925
926 }
927
mysql_db_query_ret(mysql_conn_t * mysql_conn,char * query,bool last)928 extern MYSQL_RES *mysql_db_query_ret(mysql_conn_t *mysql_conn,
929 char *query, bool last)
930 {
931 MYSQL_RES *result = NULL;
932
933 slurm_mutex_lock(&mysql_conn->lock);
934 if (_mysql_query_internal(mysql_conn->db_conn, query) != SLURM_ERROR) {
935 if (mysql_errno(mysql_conn->db_conn) == ER_NO_SUCH_TABLE)
936 goto fini;
937 else if (last)
938 result = _get_last_result(mysql_conn->db_conn);
939 else
940 result = _get_first_result(mysql_conn->db_conn);
941 /*
942 * Starting in MariaDB 10.2 many of the api commands started
943 * setting errno erroneously.
944 */
945 errno = 0;
946 if (!result && mysql_field_count(mysql_conn->db_conn)) {
947 /* should have returned data */
948 error("We should have gotten a result: '%m' '%s'",
949 mysql_error(mysql_conn->db_conn));
950 }
951 }
952
953 fini:
954 slurm_mutex_unlock(&mysql_conn->lock);
955 return result;
956 }
957
mysql_db_query_check_after(mysql_conn_t * mysql_conn,char * query)958 extern int mysql_db_query_check_after(mysql_conn_t *mysql_conn, char *query)
959 {
960 int rc = SLURM_SUCCESS;
961
962 slurm_mutex_lock(&mysql_conn->lock);
963 if ((rc = _mysql_query_internal(
964 mysql_conn->db_conn, query)) != SLURM_ERROR)
965 rc = _clear_results(mysql_conn->db_conn);
966 slurm_mutex_unlock(&mysql_conn->lock);
967 return rc;
968 }
969
mysql_db_insert_ret_id(mysql_conn_t * mysql_conn,char * query)970 extern uint64_t mysql_db_insert_ret_id(mysql_conn_t *mysql_conn, char *query)
971 {
972 uint64_t new_id = 0;
973
974 slurm_mutex_lock(&mysql_conn->lock);
975 if (_mysql_query_internal(mysql_conn->db_conn, query) != SLURM_ERROR) {
976 new_id = mysql_insert_id(mysql_conn->db_conn);
977 if (!new_id) {
978 /* should have new id */
979 error("We should have gotten a new id: %s",
980 mysql_error(mysql_conn->db_conn));
981 }
982 }
983 slurm_mutex_unlock(&mysql_conn->lock);
984 return new_id;
985
986 }
987
mysql_db_create_table(mysql_conn_t * mysql_conn,char * table_name,storage_field_t * fields,char * ending)988 extern int mysql_db_create_table(mysql_conn_t *mysql_conn, char *table_name,
989 storage_field_t *fields, char *ending)
990 {
991 char *query = NULL;
992 int i = 0, rc;
993 storage_field_t *first_field = fields;
994
995 if (!fields || !fields->name) {
996 error("Not creating an empty table");
997 return SLURM_ERROR;
998 }
999
1000 /* We have an internal table called table_defs_table which
1001 * contains the definition of each table in the database. To
1002 * speed things up we just check against that to see if
1003 * anything has changed.
1004 */
1005 query = xstrdup_printf("create table if not exists %s "
1006 "(creation_time int unsigned not null, "
1007 "mod_time int unsigned default 0 not null, "
1008 "table_name text not null, "
1009 "definition text not null, "
1010 "primary key (table_name(50))) engine='innodb'",
1011 table_defs_table);
1012 if (mysql_db_query(mysql_conn, query) == SLURM_ERROR) {
1013 xfree(query);
1014 return SLURM_ERROR;
1015 }
1016 xfree(query);
1017
1018 query = xstrdup_printf("create table if not exists %s (`%s` %s",
1019 table_name, fields->name, fields->options);
1020 i = 1;
1021 fields++;
1022
1023 while (fields && fields->name) {
1024 xstrfmtcat(query, ", `%s` %s", fields->name, fields->options);
1025 fields++;
1026 i++;
1027 }
1028 xstrcat(query, ending);
1029
1030 /* make sure we can do a rollback */
1031 xstrcat(query, " engine='innodb'");
1032
1033 if (mysql_db_query(mysql_conn, query) == SLURM_ERROR) {
1034 xfree(query);
1035 return SLURM_ERROR;
1036 }
1037 xfree(query);
1038
1039 rc = _mysql_make_table_current(
1040 mysql_conn, table_name, first_field, ending);
1041 return rc;
1042 }
1043