From c93c5666ce52439eee66d29e4357b7967f769adc Mon Sep 17 00:00:00 2001 From: Daniel Lenski Date: Wed, 5 Jul 2023 12:24:12 -0700 Subject: [PATCH] Allow clients to disable server-initiated instant failovers This adds a new boolean option, `follow-instant-failovers` (ENABLED by default), to allow the client to disable server-initiated instant failover and simply return an error message. All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc. --- include/mysql.h | 9 ++++++++ libmariadb/mariadb_lib.c | 45 ++++++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/include/mysql.h b/include/mysql.h index 76b168303..44ce6d0f8 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -227,6 +227,8 @@ extern const char *SQLSTATE_UNKNOWN; /* MariaDB specific */ MYSQL_PROGRESS_CALLBACK=5999, MYSQL_OPT_NONBLOCK, + MARIADB_OPT_FOLLOW_INSTANT_FAILOVERS, + /* MariaDB Connector/C specific */ MYSQL_DATABASE_DRIVER=7000, MARIADB_OPT_SSL_FP, /* deprecated, use MARIADB_OPT_TLS_PEER_FP instead */ @@ -337,12 +339,19 @@ struct st_mysql_options { char *bind_address; my_bool secure_auth; my_bool report_data_truncation; + my_bool follow_instant_failovers; + /* function pointers for local infile support */ int (*local_infile_init)(void **, const char *, void *); int (*local_infile_read)(void *, char *, unsigned int); void (*local_infile_end)(void *); int (*local_infile_error)(void *, char *, unsigned int); void *local_infile_userdata; + + /* WHY are some options relegated to this "extension" structure? + * What is the logic for distinguishing the "main" options from + * the extension options? + */ struct st_mysql_options_extension *extension; }; diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 4a5ecabf3..6c9ffaaf7 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -668,6 +668,7 @@ struct st_default_options mariadb_defaults[] = {{MYSQL_SET_CHARSET_NAME}, MARIADB_OPTION_STR, "default-character-set"}, {{MARIADB_OPT_INTERACTIVE}, MARIADB_OPTION_NONE, "interactive-timeout"}, {{MYSQL_OPT_CONNECT_TIMEOUT}, MARIADB_OPTION_INT, "connect-timeout"}, + {{MARIADB_OPT_FOLLOW_INSTANT_FAILOVERS}, MARIADB_OPTION_BOOL, "follow-instant-failovers"}, {{MYSQL_OPT_LOCAL_INFILE}, MARIADB_OPTION_BOOL, "local-infile"}, {{0}, 0 ,"disable-local-infile",}, {{MYSQL_OPT_SSL_CIPHER}, MARIADB_OPTION_STR, "ssl-cipher"}, @@ -1294,6 +1295,7 @@ mysql_init(MYSQL *mysql) goto error; mysql->options.report_data_truncation= 1; mysql->options.connect_timeout=CONNECT_TIMEOUT; + mysql->options.follow_instant_failovers= TRUE; mysql->charset= mysql_find_charset_name(MARIADB_DEFAULT_CHARSET); mysql->methods= &MARIADB_DEFAULT_METHODS; strcpy(mysql->net.sqlstate, "00000"); @@ -1995,23 +1997,31 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, { if (mysql->net.last_errno == ER_INSTANT_FAILOVER) { - char *p= mysql->net.last_error; /* Should look like '|message|host[:port]' */ - if (p && p[0] == '|') - p= strchr(p + 1, '|') ? : NULL; - if (p && *++p) { - host= p; - p= strchr(p, ':') ? : NULL; - if (p) { - *p++ = '\0'; - port= atoi(p); - } - else - { - /* Restore to the default port, rather than reusing our current one */ - port= 0; - } + if (!mysql->options.follow_instant_failovers) + { + /* Client has disabled instant failover. Fall through and treat this + * as a "normal" error. */ + } + else + { + char *p= mysql->net.last_error; /* Should look like '|message|host[:port]' */ + if (p && p[0] == '|') + p= strchr(p + 1, '|') ? : NULL; + if (p && *++p) { + host= p; + p= strchr(p, ':') ? : NULL; + if (p) { + *p++ = '\0'; + port= atoi(p); + } + else + { + /* Restore to the default port, rather than reusing our current one */ + port= 0; + } fprintf(stderr, "Got instant failover to '%s' (port %d)\n", host, port); - goto tcp_redirect; + goto tcp_redirect; + } } } goto error; @@ -3521,6 +3531,9 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) case MYSQL_OPT_RECONNECT: mysql->options.reconnect= *(my_bool *)arg1; break; + case MARIADB_OPT_FOLLOW_INSTANT_FAILOVERS: + mysql->options.follow_instant_failovers= *(my_bool *)arg1; + break; case MYSQL_OPT_PROTOCOL: mysql->options.protocol= *((uint *)arg1); break;