| /*****************************************************************************\ |
| * mysql_common.c - common functions for the mysql storage plugin. |
| ***************************************************************************** |
| * Copyright (C) 2004-2007 The Regents of the University of California. |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). |
| * Written by Danny Auble <da@llnl.gov> |
| * |
| * This file is part of Slurm, a resource management program. |
| * For details, see <https://slurm.schedmd.com/>. |
| * Please also read the included file: DISCLAIMER. |
| * |
| * Slurm is free software; you can redistribute it and/or modify it under |
| * the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * In addition, as a special exception, the copyright holders give permission |
| * to link the code of portions of this program with the OpenSSL library under |
| * certain conditions as described in each individual source file, and |
| * distribute linked combinations including the two. You must obey the GNU |
| * General Public License in all respects for all of the code used other than |
| * OpenSSL. If you modify file(s) with this exception, you may extend this |
| * exception to your version of the file(s), but you are not obligated to do |
| * so. If you do not wish to do so, delete this exception statement from your |
| * version. If you delete this exception statement from all source files in |
| * the program, then also delete it here. |
| * |
| * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with Slurm; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * This file is patterned after jobcomp_linux.c, written by Morris Jette and |
| * Copyright (C) 2002 The Regents of the University of California. |
| \*****************************************************************************/ |
| |
| #include "config.h" |
| |
| #include "mysql_common.h" |
| #include "src/common/log.h" |
| #include "src/common/xstring.h" |
| #include "src/common/xmalloc.h" |
| #include "src/common/timers.h" |
| #include "src/common/slurm_protocol_api.h" |
| #include "src/common/read_config.h" |
| |
| static char *table_defs_table = "table_defs_table"; |
| |
| typedef struct { |
| char *name; |
| char *columns; |
| } db_key_t; |
| |
| static void _destroy_db_key(void *arg) |
| { |
| db_key_t *db_key = (db_key_t *)arg; |
| |
| if (db_key) { |
| xfree(db_key->name); |
| xfree(db_key->columns); |
| xfree(db_key); |
| } |
| } |
| |
| /* NOTE: Ensure that mysql_conn->lock is set on function entry */ |
| static int _clear_results(MYSQL *db_conn) |
| { |
| MYSQL_RES *result = NULL; |
| int rc = 0; |
| |
| do { |
| /* did current statement return data? */ |
| if ((result = mysql_store_result(db_conn))) |
| mysql_free_result(result); |
| |
| /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */ |
| if ((rc = mysql_next_result(db_conn)) > 0) |
| error("Could not execute statement %d %s", |
| mysql_errno(db_conn), |
| mysql_error(db_conn)); |
| } while (rc == 0); |
| |
| if (rc > 0) { |
| errno = rc; |
| return SLURM_ERROR; |
| } |
| return SLURM_SUCCESS; |
| } |
| |
| /* NOTE: Ensure that mysql_conn->lock is set on function entry */ |
| static MYSQL_RES *_get_first_result(MYSQL *db_conn) |
| { |
| MYSQL_RES *result = NULL; |
| int rc = 0; |
| do { |
| /* did current statement return data? */ |
| if ((result = mysql_store_result(db_conn))) |
| return result; |
| |
| /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */ |
| if ((rc = mysql_next_result(db_conn)) > 0) |
| debug3("error: Could not execute statement %d", rc); |
| |
| } while (rc == 0); |
| |
| return NULL; |
| } |
| |
| /* NOTE: Ensure that mysql_conn->lock is set on function entry */ |
| static MYSQL_RES *_get_last_result(MYSQL *db_conn) |
| { |
| MYSQL_RES *result = NULL; |
| MYSQL_RES *last_result = NULL; |
| int rc = 0; |
| do { |
| /* did current statement return data? */ |
| if ((result = mysql_store_result(db_conn))) { |
| if (last_result) |
| mysql_free_result(last_result); |
| last_result = result; |
| } |
| /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */ |
| if ((rc = mysql_next_result(db_conn)) > 0) |
| debug3("error: Could not execute statement %d", rc); |
| } while (rc == 0); |
| |
| return last_result; |
| } |
| |
| /* NOTE: Ensure that mysql_conn->lock is set on function entry */ |
| static int _mysql_query_internal(MYSQL *db_conn, char *query) |
| { |
| int rc = SLURM_SUCCESS; |
| |
| if (!db_conn) |
| fatal("You haven't inited this storage yet."); |
| |
| /* clear out the old results so we don't get a 2014 error */ |
| _clear_results(db_conn); |
| if (mysql_query(db_conn, query)) { |
| const char *err_str = mysql_error(db_conn); |
| errno = mysql_errno(db_conn); |
| if (errno == ER_NO_SUCH_TABLE) { |
| debug4("This could happen often and is expected.\n" |
| "mysql_query failed: %d %s\n%s", |
| errno, err_str, query); |
| errno = 0; |
| goto end_it; |
| } |
| error("mysql_query failed: %d %s\n%s", errno, err_str, query); |
| if (errno == ER_LOCK_WAIT_TIMEOUT) { |
| /* FIXME: If we get ER_LOCK_WAIT_TIMEOUT here we need |
| * to restart the connections, but it appears restarting |
| * the calling program is the only way to handle this. |
| * If anyone in the future figures out a way to handle |
| * this, super. Until then we will need to restart the |
| * calling program if you ever get this error. |
| */ |
| fatal("mysql gave ER_LOCK_WAIT_TIMEOUT as an error. " |
| "The only way to fix this is restart the " |
| "calling program"); |
| } else if (errno == ER_HOST_IS_BLOCKED) { |
| fatal("MySQL gave ER_HOST_IS_BLOCKED as an error. " |
| "You will need to call 'mysqladmin flush-hosts' " |
| "to regain connectivity."); |
| } |
| rc = SLURM_ERROR; |
| } |
| end_it: |
| /* |
| * Starting in MariaDB 10.2 many of the api commands started |
| * setting errno erroneously. |
| */ |
| if (!rc) |
| errno = 0; |
| |
| return rc; |
| } |
| |
| /* NOTE: Ensure that mysql_conn->lock is NOT set on function entry */ |
| static int _mysql_make_table_current(mysql_conn_t *mysql_conn, char *table_name, |
| storage_field_t *fields, char *ending) |
| { |
| char *query = NULL; |
| char *correct_query = NULL; |
| MYSQL_RES *result = NULL; |
| MYSQL_ROW row; |
| int i = 0; |
| List columns = NULL; |
| ListIterator itr = NULL; |
| char *col = NULL; |
| int adding = 0; |
| int run_update = 0; |
| char *primary_key = NULL; |
| char *unique_index = NULL; |
| int old_primary = 0; |
| char *old_index = NULL; |
| char *temp = NULL, *temp2 = NULL; |
| List keys_list = NULL; |
| db_key_t *db_key = NULL; |
| |
| DEF_TIMERS; |
| |
| /* figure out the unique keys in the table */ |
| query = xstrdup_printf("show index from %s where non_unique=0", |
| table_name); |
| if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) { |
| xfree(query); |
| return SLURM_ERROR; |
| } |
| xfree(query); |
| while ((row = mysql_fetch_row(result))) { |
| // row[2] is the key name |
| if (!xstrcasecmp(row[2], "PRIMARY")) |
| old_primary = 1; |
| else if (!old_index) |
| old_index = xstrdup(row[2]); |
| } |
| mysql_free_result(result); |
| |
| /* figure out the non-unique keys in the table */ |
| query = xstrdup_printf("show index from %s where non_unique=1", |
| table_name); |
| if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) { |
| xfree(query); |
| xfree(old_index); |
| return SLURM_ERROR; |
| } |
| xfree(query); |
| |
| itr = NULL; |
| keys_list = list_create(_destroy_db_key); |
| while ((row = mysql_fetch_row(result))) { |
| if (!itr) |
| itr = list_iterator_create(keys_list); |
| else |
| list_iterator_reset(itr); |
| while ((db_key = list_next(itr))) { |
| if (!xstrcmp(db_key->name, row[2])) |
| break; |
| } |
| |
| if (db_key) { |
| xstrfmtcat(db_key->columns, ", %s", row[4]); |
| } else { |
| db_key = xmalloc(sizeof(db_key_t)); |
| db_key->name = xstrdup(row[2]); // name |
| db_key->columns = xstrdup(row[4]); // column name |
| list_append(keys_list, db_key); // don't use list_push |
| } |
| } |
| mysql_free_result(result); |
| |
| if (itr) { |
| list_iterator_destroy(itr); |
| itr = NULL; |
| } |
| |
| /* figure out the existing columns in the table */ |
| query = xstrdup_printf("show columns from %s", table_name); |
| if (!(result = mysql_db_query_ret(mysql_conn, query, 0))) { |
| xfree(query); |
| xfree(old_index); |
| FREE_NULL_LIST(keys_list); |
| return SLURM_ERROR; |
| } |
| xfree(query); |
| columns = list_create(slurm_destroy_char); |
| while ((row = mysql_fetch_row(result))) { |
| col = xstrdup(row[0]); //Field |
| list_append(columns, col); |
| } |
| mysql_free_result(result); |
| |
| |
| itr = list_iterator_create(columns); |
| /* In MySQL 5.7.4 we lost the ability to run 'alter ignore'. This was |
| * needed when converting old tables to new schemas. If people convert |
| * in the future from an older version of Slurm that needed the ignore |
| * to work they will have to downgrade mysql to <= 5.7.3 to make things |
| * work correctly or manually edit the database to get things to work. |
| */ |
| query = xstrdup_printf("alter table %s", table_name); |
| correct_query = xstrdup(query); |
| START_TIMER; |
| while (fields[i].name) { |
| int found = 0; |
| |
| list_iterator_reset(itr); |
| while ((col = list_next(itr))) { |
| if (!xstrcmp(col, fields[i].name)) { |
| xstrfmtcat(query, " modify `%s` %s,", |
| fields[i].name, |
| fields[i].options); |
| xstrfmtcat(correct_query, " modify `%s` %s,", |
| fields[i].name, |
| fields[i].options); |
| list_delete_item(itr); |
| found = 1; |
| break; |
| } |
| } |
| if (!found) { |
| if (i) { |
| info("adding column %s after %s in table %s", |
| fields[i].name, |
| fields[i-1].name, |
| table_name); |
| xstrfmtcat(query, " add `%s` %s after %s,", |
| fields[i].name, |
| fields[i].options, |
| fields[i-1].name); |
| xstrfmtcat(correct_query, " modify `%s` %s,", |
| fields[i].name, |
| fields[i].options); |
| } else { |
| info("adding column %s at the beginning " |
| "of table %s", |
| fields[i].name, |
| table_name); |
| xstrfmtcat(query, " add `%s` %s first,", |
| fields[i].name, |
| fields[i].options); |
| xstrfmtcat(correct_query, " modify `%s` %s,", |
| fields[i].name, |
| fields[i].options); |
| } |
| adding = 1; |
| } |
| |
| i++; |
| } |
| |
| list_iterator_reset(itr); |
| while ((col = list_next(itr))) { |
| adding = 1; |
| info("dropping column %s from table %s", col, table_name); |
| xstrfmtcat(query, " drop %s,", col); |
| } |
| |
| list_iterator_destroy(itr); |
| FREE_NULL_LIST(columns); |
| |
| if ((temp = strstr(ending, "primary key ("))) { |
| int open = 0, close =0; |
| int end = 0; |
| while (temp[end++]) { |
| if (temp[end] == '(') |
| open++; |
| else if (temp[end] == ')') |
| close++; |
| else |
| continue; |
| if (open == close) |
| break; |
| } |
| if (temp[end]) { |
| end++; |
| primary_key = xstrndup(temp, end); |
| if (old_primary) { |
| xstrcat(query, " drop primary key,"); |
| xstrcat(correct_query, " drop primary key,"); |
| } |
| xstrfmtcat(query, " add %s,", primary_key); |
| xstrfmtcat(correct_query, " add %s,", primary_key); |
| |
| xfree(primary_key); |
| } |
| } |
| |
| if ((temp = strstr(ending, "unique index ("))) { |
| int open = 0, close = 0; |
| int end = 0; |
| while (temp[end++]) { |
| if (temp[end] == '(') |
| open++; |
| else if (temp[end] == ')') |
| close++; |
| else |
| continue; |
| if (open == close) |
| break; |
| } |
| if (temp[end]) { |
| end++; |
| unique_index = xstrndup(temp, end); |
| if (old_index) { |
| xstrfmtcat(query, " drop index %s,", |
| old_index); |
| xstrfmtcat(correct_query, " drop index %s,", |
| old_index); |
| } |
| xstrfmtcat(query, " add %s,", unique_index); |
| xstrfmtcat(correct_query, " add %s,", unique_index); |
| xfree(unique_index); |
| } |
| } |
| xfree(old_index); |
| |
| temp2 = ending; |
| itr = list_iterator_create(keys_list); |
| while ((temp = strstr(temp2, ", key "))) { |
| int open = 0, close = 0, name_end = 0; |
| int end = 5; |
| char *new_key_name = NULL, *new_key = NULL; |
| while (temp[end++]) { |
| if (!name_end && (temp[end] == ' ')) { |
| name_end = end; |
| continue; |
| } else if (temp[end] == '(') { |
| open++; |
| if (!name_end) |
| name_end = end; |
| } else if (temp[end] == ')') |
| close++; |
| else |
| continue; |
| if (open == close) |
| break; |
| } |
| if (temp[end]) { |
| end++; |
| new_key_name = xstrndup(temp+6, name_end-6); |
| new_key = xstrndup(temp+2, end-2); // skip ', ' |
| while ((db_key = list_next(itr))) { |
| if (!xstrcmp(db_key->name, new_key_name)) { |
| list_remove(itr); |
| break; |
| } |
| } |
| list_iterator_reset(itr); |
| if (db_key) { |
| xstrfmtcat(query, |
| " drop key %s,", db_key->name); |
| xstrfmtcat(correct_query, |
| " drop key %s,", db_key->name); |
| _destroy_db_key(db_key); |
| } else { |
| xstrfmtcat(correct_query, |
| " drop key %s,", new_key_name); |
| info("adding %s to table %s", |
| new_key, table_name); |
| } |
| |
| xstrfmtcat(query, " add %s,", new_key); |
| xstrfmtcat(correct_query, " add %s,", new_key); |
| |
| xfree(new_key); |
| xfree(new_key_name); |
| } |
| temp2 = temp + end; |
| } |
| |
| /* flush extra (old) keys */ |
| while ((db_key = list_next(itr))) { |
| info("dropping key %s from table %s", db_key->name, table_name); |
| xstrfmtcat(query, " drop key %s,", db_key->name); |
| } |
| list_iterator_destroy(itr); |
| |
| FREE_NULL_LIST(keys_list); |
| |
| query[strlen(query)-1] = ';'; |
| correct_query[strlen(correct_query)-1] = ';'; |
| //info("%d query\n%s", __LINE__, query); |
| |
| /* see if we have already done this definition */ |
| if (!adding) { |
| char *quoted = slurm_add_slash_to_quotes(query); |
| char *query2 = xstrdup_printf("select table_name from " |
| "%s where definition='%s'", |
| table_defs_table, quoted); |
| MYSQL_RES *result = NULL; |
| MYSQL_ROW row; |
| |
| xfree(quoted); |
| run_update = 1; |
| if ((result = mysql_db_query_ret(mysql_conn, query2, 0))) { |
| if ((row = mysql_fetch_row(result))) |
| run_update = 0; |
| mysql_free_result(result); |
| } |
| xfree(query2); |
| if (run_update) { |
| run_update = 2; |
| query2 = xstrdup_printf("select table_name from " |
| "%s where table_name='%s'", |
| table_defs_table, table_name); |
| if ((result = mysql_db_query_ret( |
| mysql_conn, query2, 0))) { |
| if ((row = mysql_fetch_row(result))) |
| run_update = 1; |
| mysql_free_result(result); |
| } |
| xfree(query2); |
| } |
| } |
| |
| /* if something has changed run the alter line */ |
| if (run_update || adding) { |
| time_t now = time(NULL); |
| char *query2 = NULL; |
| char *quoted = NULL; |
| |
| if (run_update == 2) |
| debug4("Table %s doesn't exist, adding", table_name); |
| else |
| debug("Table %s has changed. Updating...", table_name); |
| if (mysql_db_query(mysql_conn, query)) { |
| xfree(query); |
| return SLURM_ERROR; |
| } |
| quoted = slurm_add_slash_to_quotes(correct_query); |
| query2 = xstrdup_printf("insert into %s (creation_time, " |
| "mod_time, table_name, definition) " |
| "values (%ld, %ld, '%s', '%s') " |
| "on duplicate key update " |
| "definition='%s', mod_time=%ld;", |
| table_defs_table, now, now, |
| table_name, quoted, |
| quoted, now); |
| xfree(quoted); |
| if (mysql_db_query(mysql_conn, query2)) { |
| xfree(query2); |
| return SLURM_ERROR; |
| } |
| xfree(query2); |
| } |
| |
| xfree(query); |
| xfree(correct_query); |
| query = xstrdup_printf("make table current %s", table_name); |
| END_TIMER2(query); |
| xfree(query); |
| return SLURM_SUCCESS; |
| } |
| |
| /* NOTE: Ensure that mysql_conn->lock is set on function entry */ |
| static int _create_db(char *db_name, mysql_db_info_t *db_info) |
| { |
| MYSQL *mysql_db = NULL; |
| int rc = SLURM_ERROR; |
| |
| MYSQL *db_ptr = NULL; |
| char *db_host = NULL; |
| |
| while (rc == SLURM_ERROR) { |
| rc = SLURM_SUCCESS; |
| if (!(mysql_db = mysql_init(mysql_db))) |
| fatal("mysql_init failed: %s", mysql_error(mysql_db)); |
| |
| db_host = db_info->host; |
| db_ptr = mysql_real_connect(mysql_db, |
| db_host, db_info->user, |
| db_info->pass, NULL, |
| db_info->port, NULL, 0); |
| |
| if (!db_ptr && db_info->backup) { |
| info("Connection failed to host = %s " |
| "user = %s port = %u", |
| db_host, db_info->user, |
| db_info->port); |
| db_host = db_info->backup; |
| db_ptr = mysql_real_connect(mysql_db, db_host, |
| db_info->user, |
| db_info->pass, NULL, |
| db_info->port, NULL, 0); |
| } |
| |
| if (db_ptr) { |
| char *create_line = NULL; |
| xstrfmtcat(create_line, "create database %s", db_name); |
| if (mysql_query(mysql_db, create_line)) { |
| fatal("mysql_query failed: %d %s\n%s", |
| mysql_errno(mysql_db), |
| mysql_error(mysql_db), create_line); |
| } |
| xfree(create_line); |
| if (mysql_thread_safe()) |
| mysql_thread_end(); |
| mysql_close(mysql_db); |
| } else { |
| info("Connection failed to host = %s " |
| "user = %s port = %u", |
| db_host, db_info->user, |
| db_info->port); |
| error("mysql_real_connect failed: %d %s", |
| mysql_errno(mysql_db), |
| mysql_error(mysql_db)); |
| rc = SLURM_ERROR; |
| } |
| if (rc == SLURM_ERROR) |
| sleep(3); |
| } |
| return rc; |
| } |
| |
| extern mysql_conn_t *create_mysql_conn(int conn_num, bool rollback, |
| char *cluster_name) |
| { |
| mysql_conn_t *mysql_conn = xmalloc(sizeof(mysql_conn_t)); |
| |
| mysql_conn->rollback = rollback; |
| mysql_conn->conn = conn_num; |
| mysql_conn->cluster_name = xstrdup(cluster_name); |
| slurm_mutex_init(&mysql_conn->lock); |
| mysql_conn->update_list = list_create(slurmdb_destroy_update_object); |
| |
| return mysql_conn; |
| } |
| |
| extern int destroy_mysql_conn(mysql_conn_t *mysql_conn) |
| { |
| if (mysql_conn) { |
| mysql_db_close_db_connection(mysql_conn); |
| xfree(mysql_conn->pre_commit_query); |
| xfree(mysql_conn->cluster_name); |
| slurm_mutex_destroy(&mysql_conn->lock); |
| FREE_NULL_LIST(mysql_conn->update_list); |
| xfree(mysql_conn); |
| } |
| |
| return SLURM_SUCCESS; |
| } |
| |
| extern mysql_db_info_t *create_mysql_db_info(slurm_mysql_plugin_type_t type) |
| { |
| mysql_db_info_t *db_info = xmalloc(sizeof(mysql_db_info_t)); |
| |
| switch (type) { |
| case SLURM_MYSQL_PLUGIN_AS: |
| db_info->port = slurm_get_accounting_storage_port(); |
| if (!db_info->port) { |
| db_info->port = DEFAULT_MYSQL_PORT; |
| slurm_set_accounting_storage_port(db_info->port); |
| } |
| db_info->host = slurm_get_accounting_storage_host(); |
| db_info->backup = slurm_get_accounting_storage_backup_host(); |
| db_info->user = slurm_get_accounting_storage_user(); |
| db_info->pass = slurm_get_accounting_storage_pass(); |
| break; |
| case SLURM_MYSQL_PLUGIN_JC: |
| db_info->port = slurm_get_jobcomp_port(); |
| if (!db_info->port) { |
| db_info->port = DEFAULT_MYSQL_PORT; |
| slurm_set_jobcomp_port(db_info->port); |
| } |
| db_info->host = slurm_get_jobcomp_host(); |
| db_info->user = slurm_get_jobcomp_user(); |
| db_info->pass = slurm_get_jobcomp_pass(); |
| break; |
| default: |
| xfree(db_info); |
| fatal("Unknown mysql_db_info %d", type); |
| } |
| return db_info; |
| } |
| |
| extern int destroy_mysql_db_info(mysql_db_info_t *db_info) |
| { |
| if (db_info) { |
| xfree(db_info->backup); |
| xfree(db_info->host); |
| xfree(db_info->user); |
| xfree(db_info->pass); |
| xfree(db_info); |
| } |
| return SLURM_SUCCESS; |
| } |
| |
| extern int mysql_db_get_db_connection(mysql_conn_t *mysql_conn, char *db_name, |
| mysql_db_info_t *db_info) |
| { |
| int rc = SLURM_SUCCESS; |
| bool storage_init = false; |
| char *db_host = db_info->host; |
| |
| xassert(mysql_conn); |
| |
| slurm_mutex_lock(&mysql_conn->lock); |
| |
| if (!(mysql_conn->db_conn = mysql_init(mysql_conn->db_conn))) { |
| slurm_mutex_unlock(&mysql_conn->lock); |
| fatal("mysql_init failed: %s", |
| mysql_error(mysql_conn->db_conn)); |
| } else { |
| /* If this ever changes you will need to alter |
| * src/common/slurmdbd_defs.c function _send_init_msg to |
| * handle a different timeout when polling for the |
| * response. |
| */ |
| unsigned int my_timeout = 30; |
| #ifdef MYSQL_OPT_RECONNECT |
| my_bool reconnect = 1; |
| /* make sure reconnect is on */ |
| mysql_options(mysql_conn->db_conn, MYSQL_OPT_RECONNECT, |
| &reconnect); |
| #endif |
| mysql_options(mysql_conn->db_conn, MYSQL_OPT_CONNECT_TIMEOUT, |
| (char *)&my_timeout); |
| while (!storage_init) { |
| debug2("Attempting to connect to %s:%d", db_host, |
| db_info->port); |
| if (!mysql_real_connect(mysql_conn->db_conn, db_host, |
| db_info->user, db_info->pass, |
| db_name, db_info->port, NULL, |
| CLIENT_MULTI_STATEMENTS)) { |
| int err = mysql_errno(mysql_conn->db_conn); |
| if (err == ER_BAD_DB_ERROR) { |
| debug("Database %s not created. " |
| "Creating", db_name); |
| rc = _create_db(db_name, db_info); |
| } else { |
| const char *err_str = mysql_error( |
| mysql_conn->db_conn); |
| if ((db_host == db_info->host) |
| && db_info->backup) { |
| debug2("mysql_real_connect failed: %d %s", |
| err, err_str); |
| db_host = db_info->backup; |
| continue; |
| } |
| |
| error("mysql_real_connect failed: " |
| "%d %s", |
| err, err_str); |
| rc = ESLURM_DB_CONNECTION; |
| mysql_close(mysql_conn->db_conn); |
| mysql_conn->db_conn = NULL; |
| break; |
| } |
| } else { |
| storage_init = true; |
| if (mysql_conn->rollback) |
| mysql_autocommit( |
| mysql_conn->db_conn, 0); |
| rc = _mysql_query_internal( |
| mysql_conn->db_conn, |
| "SET session sql_mode='ANSI_QUOTES," |
| "NO_ENGINE_SUBSTITUTION';"); |
| } |
| } |
| } |
| slurm_mutex_unlock(&mysql_conn->lock); |
| errno = rc; |
| return rc; |
| } |
| |
| extern int mysql_db_close_db_connection(mysql_conn_t *mysql_conn) |
| { |
| slurm_mutex_lock(&mysql_conn->lock); |
| if (mysql_conn && mysql_conn->db_conn) { |
| if (mysql_thread_safe()) |
| mysql_thread_end(); |
| mysql_close(mysql_conn->db_conn); |
| mysql_conn->db_conn = NULL; |
| } |
| slurm_mutex_unlock(&mysql_conn->lock); |
| return SLURM_SUCCESS; |
| } |
| |
| extern int mysql_db_cleanup() |
| { |
| debug3("starting mysql cleaning up"); |
| |
| #ifdef mysql_library_end |
| mysql_library_end(); |
| #else |
| mysql_server_end(); |
| #endif |
| debug3("finished mysql cleaning up"); |
| return SLURM_SUCCESS; |
| } |
| |
| extern int mysql_db_query(mysql_conn_t *mysql_conn, char *query) |
| { |
| int rc = SLURM_SUCCESS; |
| |
| if (!mysql_conn || !mysql_conn->db_conn) { |
| fatal("You haven't inited this storage yet."); |
| return 0; /* For CLANG false positive */ |
| } |
| slurm_mutex_lock(&mysql_conn->lock); |
| rc = _mysql_query_internal(mysql_conn->db_conn, query); |
| slurm_mutex_unlock(&mysql_conn->lock); |
| return rc; |
| } |
| |
| /* |
| * Executes a single delete sql query. |
| * Returns the number of deleted rows, <0 for failure. |
| */ |
| extern int mysql_db_delete_affected_rows(mysql_conn_t *mysql_conn, char *query) |
| { |
| int rc = SLURM_SUCCESS; |
| |
| if (!mysql_conn || !mysql_conn->db_conn) { |
| fatal("You haven't inited this storage yet."); |
| return 0; /* For CLANG false positive */ |
| } |
| slurm_mutex_lock(&mysql_conn->lock); |
| if (!(rc = _mysql_query_internal(mysql_conn->db_conn, query))) |
| rc = mysql_affected_rows(mysql_conn->db_conn); |
| slurm_mutex_unlock(&mysql_conn->lock); |
| return rc; |
| } |
| |
| extern int mysql_db_ping(mysql_conn_t *mysql_conn) |
| { |
| int rc; |
| |
| if (!mysql_conn->db_conn) |
| return -1; |
| |
| /* clear out the old results so we don't get a 2014 error */ |
| slurm_mutex_lock(&mysql_conn->lock); |
| _clear_results(mysql_conn->db_conn); |
| rc = mysql_ping(mysql_conn->db_conn); |
| /* |
| * Starting in MariaDB 10.2 many of the api commands started |
| * setting errno erroneously. |
| */ |
| if (!rc) |
| errno = 0; |
| slurm_mutex_unlock(&mysql_conn->lock); |
| return rc; |
| } |
| |
| extern int mysql_db_commit(mysql_conn_t *mysql_conn) |
| { |
| int rc = SLURM_SUCCESS; |
| |
| if (!mysql_conn->db_conn) |
| return SLURM_ERROR; |
| |
| slurm_mutex_lock(&mysql_conn->lock); |
| /* clear out the old results so we don't get a 2014 error */ |
| _clear_results(mysql_conn->db_conn); |
| if (mysql_commit(mysql_conn->db_conn)) { |
| error("mysql_commit failed: %d %s", |
| mysql_errno(mysql_conn->db_conn), |
| mysql_error(mysql_conn->db_conn)); |
| errno = mysql_errno(mysql_conn->db_conn); |
| rc = SLURM_ERROR; |
| } |
| slurm_mutex_unlock(&mysql_conn->lock); |
| return rc; |
| } |
| |
| extern int mysql_db_rollback(mysql_conn_t *mysql_conn) |
| { |
| int rc = SLURM_SUCCESS; |
| |
| if (!mysql_conn->db_conn) |
| return SLURM_ERROR; |
| |
| slurm_mutex_lock(&mysql_conn->lock); |
| /* clear out the old results so we don't get a 2014 error */ |
| _clear_results(mysql_conn->db_conn); |
| if (mysql_rollback(mysql_conn->db_conn)) { |
| error("mysql_commit failed: %d %s", |
| mysql_errno(mysql_conn->db_conn), |
| mysql_error(mysql_conn->db_conn)); |
| errno = mysql_errno(mysql_conn->db_conn); |
| rc = SLURM_ERROR; |
| } else { |
| /* |
| * Starting in MariaDB 10.2 many of the api commands started |
| * setting errno erroneously. |
| */ |
| errno = 0; |
| } |
| slurm_mutex_unlock(&mysql_conn->lock); |
| return rc; |
| |
| } |
| |
| extern MYSQL_RES *mysql_db_query_ret(mysql_conn_t *mysql_conn, |
| char *query, bool last) |
| { |
| MYSQL_RES *result = NULL; |
| |
| slurm_mutex_lock(&mysql_conn->lock); |
| if (_mysql_query_internal(mysql_conn->db_conn, query) != SLURM_ERROR) { |
| if (mysql_errno(mysql_conn->db_conn) == ER_NO_SUCH_TABLE) |
| goto fini; |
| else if (last) |
| result = _get_last_result(mysql_conn->db_conn); |
| else |
| result = _get_first_result(mysql_conn->db_conn); |
| /* |
| * Starting in MariaDB 10.2 many of the api commands started |
| * setting errno erroneously. |
| */ |
| errno = 0; |
| if (!result && mysql_field_count(mysql_conn->db_conn)) { |
| /* should have returned data */ |
| error("We should have gotten a result: '%m' '%s'", |
| mysql_error(mysql_conn->db_conn)); |
| } |
| } |
| |
| fini: |
| slurm_mutex_unlock(&mysql_conn->lock); |
| return result; |
| } |
| |
| extern int mysql_db_query_check_after(mysql_conn_t *mysql_conn, char *query) |
| { |
| int rc = SLURM_SUCCESS; |
| |
| slurm_mutex_lock(&mysql_conn->lock); |
| if ((rc = _mysql_query_internal( |
| mysql_conn->db_conn, query)) != SLURM_ERROR) |
| rc = _clear_results(mysql_conn->db_conn); |
| slurm_mutex_unlock(&mysql_conn->lock); |
| return rc; |
| } |
| |
| extern uint64_t mysql_db_insert_ret_id(mysql_conn_t *mysql_conn, char *query) |
| { |
| uint64_t new_id = 0; |
| |
| slurm_mutex_lock(&mysql_conn->lock); |
| if (_mysql_query_internal(mysql_conn->db_conn, query) != SLURM_ERROR) { |
| new_id = mysql_insert_id(mysql_conn->db_conn); |
| if (!new_id) { |
| /* should have new id */ |
| error("We should have gotten a new id: %s", |
| mysql_error(mysql_conn->db_conn)); |
| } |
| } |
| slurm_mutex_unlock(&mysql_conn->lock); |
| return new_id; |
| |
| } |
| |
| extern int mysql_db_create_table(mysql_conn_t *mysql_conn, char *table_name, |
| storage_field_t *fields, char *ending) |
| { |
| char *query = NULL; |
| int i = 0, rc; |
| storage_field_t *first_field = fields; |
| |
| if (!fields || !fields->name) { |
| error("Not creating an empty table"); |
| return SLURM_ERROR; |
| } |
| |
| /* We have an internal table called table_defs_table which |
| * contains the definition of each table in the database. To |
| * speed things up we just check against that to see if |
| * anything has changed. |
| */ |
| query = xstrdup_printf("create table if not exists %s " |
| "(creation_time int unsigned not null, " |
| "mod_time int unsigned default 0 not null, " |
| "table_name text not null, " |
| "definition text not null, " |
| "primary key (table_name(50))) engine='innodb'", |
| table_defs_table); |
| if (mysql_db_query(mysql_conn, query) == SLURM_ERROR) { |
| xfree(query); |
| return SLURM_ERROR; |
| } |
| xfree(query); |
| |
| query = xstrdup_printf("create table if not exists %s (`%s` %s", |
| table_name, fields->name, fields->options); |
| i = 1; |
| fields++; |
| |
| while (fields && fields->name) { |
| xstrfmtcat(query, ", `%s` %s", fields->name, fields->options); |
| fields++; |
| i++; |
| } |
| xstrcat(query, ending); |
| |
| /* make sure we can do a rollback */ |
| xstrcat(query, " engine='innodb'"); |
| |
| if (mysql_db_query(mysql_conn, query) == SLURM_ERROR) { |
| xfree(query); |
| return SLURM_ERROR; |
| } |
| xfree(query); |
| |
| rc = _mysql_make_table_current( |
| mysql_conn, table_name, first_field, ending); |
| return rc; |
| } |