diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result index 77d6d10b3e6..ac96220a805 100644 --- a/mysql-test/main/mysqld--help.result +++ b/mysql-test/main/mysqld--help.result @@ -1064,6 +1064,8 @@ The following specify which files/extra groups are read (specified before remain --read-rnd-buffer-size=# When reading rows in sorted order after a sort, the rows are read through this buffer to avoid a disk seeks + --redirect-url=name URL of another server to redirect clients to. Empty + string means no redirection --relay-log=name The location and name to use for relay logs. --relay-log-index=name The location and name to use for the file that keeps a @@ -1838,6 +1840,7 @@ read-binlog-speed-limit 0 read-buffer-size 131072 read-only FALSE read-rnd-buffer-size 262144 +redirect-url relay-log (No default value) relay-log-index (No default value) relay-log-info-file relay-log.info @@ -1869,7 +1872,7 @@ secure-timestamp NO server-id 1 session-track-schema TRUE session-track-state-change FALSE -session-track-system-variables autocommit,character_set_client,character_set_connection,character_set_results,time_zone +session-track-system-variables autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone session-track-transaction-info OFF show-slave-auth-info FALSE silent-startup FALSE diff --git a/mysql-test/suite/sys_vars/r/mdev_32254.result b/mysql-test/suite/sys_vars/r/mdev_32254.result new file mode 100644 index 00000000000..5188c56e22f --- /dev/null +++ b/mysql-test/suite/sys_vars/r/mdev_32254.result @@ -0,0 +1,13 @@ +# +# MDEV-32254 Server crashes when adding records to table after setting redirect_url with empty variable +# +set @old_redirect_url=@@global.redirect_url; +set global redirect_url=@empty_value; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'NULL' +CREATE TABLE t (c1 INT) ENGINE=INNODB; +INSERT INTO t VALUES (1),(1); +drop table t; +set global redirect_url=@old_redirect_url; +# +# end of test mdev_32254 +# diff --git a/mysql-test/suite/sys_vars/r/redirect.result b/mysql-test/suite/sys_vars/r/redirect.result new file mode 100644 index 00000000000..7d704de3bad --- /dev/null +++ b/mysql-test/suite/sys_vars/r/redirect.result @@ -0,0 +1,81 @@ +# +# MDEV-15935 Connection Redirection Mechanism in MariaDB Client/Server Protocol +# +connect con,localhost,anyone_but_root; +select @@redirect_url; +@@redirect_url +mysql://foobar +connection default; +set @old_global_redirect_url=@@global.redirect_url; +set @old_session_redirect_url=@@session.redirect_url; +set @old_session_track_system_variables=@@session_track_system_variables; +set session_track_system_variables=""; +select @@global.redirect_url; +@@global.redirect_url + +set global redirect_url=default; +select @@global.redirect_url; +@@global.redirect_url + +set global redirect_url="mariadb.org"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'mariadb.org' +set global redirect_url="https://mariadb.org"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'https://mariadb.org' +set global redirect_url="mysql://mariadb.org:"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'mysql://mariadb.org:' +set global redirect_url="mysql://mariadb.org:hello"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'mysql://mariadb.org:hello' +set global redirect_url="mysql://"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'mysql://' +set global redirect_url="mysql://mariadb.org"; +select @@global.redirect_url; +@@global.redirect_url +mysql://mariadb.org +set global redirect_url="mysql://mariadb.org:12a"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'mysql://mariadb.org:12a' +set global redirect_url="mysql://mariadb.org:66666"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'mysql://mariadb.org:66666' +set global redirect_url="mysql://mariadb.org:12345"; +select @@global.redirect_url; +@@global.redirect_url +mysql://mariadb.org:12345 +set global redirect_url="maria"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'maria' +set global redirect_url="mariadb://mariadb.org:"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'mariadb://mariadb.org:' +set global redirect_url="mariadb://mariadb.org:hello"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'mariadb://mariadb.org:hello' +set global redirect_url="mariadb://"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'mariadb://' +set global redirect_url="mariadb://mariadb.org"; +select @@global.redirect_url; +@@global.redirect_url +mariadb://mariadb.org +set global redirect_url="mariadb://mariadb.org:12a"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'mariadb://mariadb.org:12a' +set global redirect_url="mariadb://mariadb.org:66666"; +ERROR 42000: Variable 'redirect_url' can't be set to the value of 'mariadb://mariadb.org:66666' +set global redirect_url="mariadb://mariadb.org:12345"; +select @@global.redirect_url; +@@global.redirect_url +mariadb://mariadb.org:12345 +select @@session.redirect_url; +@@session.redirect_url + +set session redirect_url=default; +select @@session.redirect_url; +@@session.redirect_url +mariadb://mariadb.org:12345 +set session redirect_url="mysql://localhost"; +select @@session.redirect_url; +@@session.redirect_url +mysql://localhost +select @@global.redirect_url; +@@global.redirect_url +mariadb://mariadb.org:12345 +set global redirect_url=@old_global_redirect_url; +set session redirect_url=@old_session_redirect_url; +set session session_track_system_variables=@old_session_track_system_variables; +# +# end of test MDEV-15935 +# diff --git a/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result b/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result index c3f4a4ee4eb..4208a2e3540 100644 --- a/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result +++ b/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result @@ -5,20 +5,20 @@ # Global - default SELECT @@global.session_track_system_variables; @@global.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone +autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone # Session - default SELECT @@session.session_track_system_variables; @@session.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone +autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone # via INFORMATION_SCHEMA.GLOBAL_VARIABLES SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track_system_variables' ORDER BY VARIABLE_NAME; VARIABLE_NAME VARIABLE_VALUE -SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone # via INFORMATION_SCHEMA.SESSION_VARIABLES SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track_system_variables' ORDER BY VARIABLE_NAME; VARIABLE_NAME VARIABLE_VALUE -SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone SET @global_saved_tmp = @@global.session_track_system_variables; # Altering global variable's value @@ -28,7 +28,7 @@ SELECT @@global.session_track_system_variables; autocommit SELECT @@session.session_track_system_variables; @@session.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone +autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone # Altering session variable's value SET @@session.session_track_system_variables='autocommit'; @@ -66,25 +66,25 @@ SET @@session.session_track_system_variables = DEFAULT; SELECT @@global.session_track_system_variables; @@global.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone +autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone SELECT @@session.session_track_system_variables; @@session.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone +autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone # Variables' values in a new session (con2). connect con2,"127.0.0.1",root,,test,$MASTER_MYPORT,; SELECT @@global.session_track_system_variables; @@global.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone +autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone SELECT @@session.session_track_system_variables; @@session.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone +autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone # Altering session should not affect global. SET @@session.session_track_system_variables = 'sql_mode'; SELECT @@global.session_track_system_variables; @@global.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone +autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone SELECT @@session.session_track_system_variables; @@session.session_track_system_variables sql_mode @@ -98,7 +98,7 @@ SELECT @@global.session_track_system_variables; sql_mode SELECT @@session.session_track_system_variables; @@session.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone +autocommit,character_set_client,character_set_connection,character_set_results,redirect_url,time_zone # Switching to the default connection. connection default; diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 1fb6b245ace..c8087432b88 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -3412,6 +3412,16 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME REDIRECT_URL +VARIABLE_SCOPE SESSION +VARIABLE_TYPE VARCHAR +VARIABLE_COMMENT URL of another server to redirect clients to. Empty string means no redirection +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME RELAY_LOG VARIABLE_SCOPE GLOBAL VARIABLE_TYPE VARCHAR diff --git a/mysql-test/suite/sys_vars/t/mdev_32254.test b/mysql-test/suite/sys_vars/t/mdev_32254.test new file mode 100644 index 00000000000..d907806f430 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/mdev_32254.test @@ -0,0 +1,17 @@ +--echo # +--echo # MDEV-32254 Server crashes when adding records to table after setting redirect_url with empty variable +--echo # +--source include/have_innodb.inc +# redirect_url is undefined in embedded. +--source include/not_embedded.inc +set @old_redirect_url=@@global.redirect_url; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url=@empty_value; +CREATE TABLE t (c1 INT) ENGINE=INNODB; +INSERT INTO t VALUES (1),(1); +drop table t; +set global redirect_url=@old_redirect_url; + +--echo # +--echo # end of test mdev_32254 +--echo # diff --git a/mysql-test/suite/sys_vars/t/redirect.opt b/mysql-test/suite/sys_vars/t/redirect.opt new file mode 100644 index 00000000000..4cf52749d3a --- /dev/null +++ b/mysql-test/suite/sys_vars/t/redirect.opt @@ -0,0 +1 @@ +--init-connect="set redirect_url='mysql://foobar'" diff --git a/mysql-test/suite/sys_vars/t/redirect.test b/mysql-test/suite/sys_vars/t/redirect.test new file mode 100644 index 00000000000..5a0cb442737 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/redirect.test @@ -0,0 +1,74 @@ +--echo # +--echo # MDEV-15935 Connection Redirection Mechanism in MariaDB Client/Server Protocol +--echo # +# redirect_url is undefined in embedded. +--source include/not_embedded.inc + +# We need to connect as a non super user for the init-connect to take +# effect +--source include/add_anonymous_users.inc +connect (con,localhost,anyone_but_root); +select @@redirect_url; + +connection default; +--source include/delete_anonymous_users.inc +set @old_global_redirect_url=@@global.redirect_url; +set @old_session_redirect_url=@@session.redirect_url; +set @old_session_track_system_variables=@@session_track_system_variables; +set session_track_system_variables=""; + +select @@global.redirect_url; +set global redirect_url=default; +select @@global.redirect_url; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="mariadb.org"; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="https://mariadb.org"; + +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="mysql://mariadb.org:"; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="mysql://mariadb.org:hello"; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="mysql://"; +set global redirect_url="mysql://mariadb.org"; +select @@global.redirect_url; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="mysql://mariadb.org:12a"; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="mysql://mariadb.org:66666"; +set global redirect_url="mysql://mariadb.org:12345"; +select @@global.redirect_url; + +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="maria"; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="mariadb://mariadb.org:"; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="mariadb://mariadb.org:hello"; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="mariadb://"; +set global redirect_url="mariadb://mariadb.org"; +select @@global.redirect_url; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="mariadb://mariadb.org:12a"; +--error ER_WRONG_VALUE_FOR_VAR +set global redirect_url="mariadb://mariadb.org:66666"; +set global redirect_url="mariadb://mariadb.org:12345"; +select @@global.redirect_url; + +select @@session.redirect_url; +# Test that session default is global value +set session redirect_url=default; +select @@session.redirect_url; +set session redirect_url="mysql://localhost"; +select @@session.redirect_url; +select @@global.redirect_url; + +set global redirect_url=@old_global_redirect_url; +set session redirect_url=@old_session_redirect_url; +set session session_track_system_variables=@old_session_track_system_variables; + +--echo # +--echo # end of test MDEV-15935 +--echo # diff --git a/sql/sql_class.h b/sql/sql_class.h index 953fa9d4cfa..b28a1790030 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -883,6 +883,7 @@ typedef struct system_variables Time_zone *time_zone; char *session_track_system_variables; + char *redirect_url; /* Some wsrep variables */ ulonglong wsrep_trx_fragment_size; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 5343c01ecdb..8bab244c709 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -3278,6 +3278,8 @@ void plugin_thdvar_init(THD *thd) /* This and all other variable cleanups are here for COM_CHANGE_USER :( */ #ifndef EMBEDDED_LIBRARY thd->session_tracker.sysvars.deinit(thd); + my_free(thd->variables.redirect_url); + thd->variables.redirect_url= 0; #endif my_free((char*) thd->variables.default_master_connection.str); thd->variables.default_master_connection.str= 0; @@ -3311,7 +3313,12 @@ void plugin_thdvar_init(THD *thd) MYF(MY_WME | MY_THREAD_SPECIFIC)); #ifndef EMBEDDED_LIBRARY thd->session_tracker.sysvars.init(thd); + thd->variables.redirect_url= + my_strdup(key_memory_Sys_var_charptr_value, + global_system_variables.redirect_url, + MYF(MY_WME | MY_THREAD_SPECIFIC)); #endif + DBUG_VOID_RETURN; } @@ -3379,6 +3386,8 @@ void plugin_thdvar_cleanup(THD *thd) #ifndef EMBEDDED_LIBRARY thd->session_tracker.sysvars.deinit(thd); + my_free(thd->variables.redirect_url); + thd->variables.redirect_url= 0; #endif my_free((char*) thd->variables.default_master_connection.str); thd->variables.default_master_connection.str= 0; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 5eb11762c16..515dbb95886 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -6894,13 +6894,73 @@ static Sys_var_ulonglong Sys_max_session_mem_used( DEFAULT(LONGLONG_MAX), BLOCK_SIZE(1)); #ifndef EMBEDDED_LIBRARY +/** + Validate a redirect_url string. + + A valid string is either empty, or of the format mysql://host[:port], + where host is an arbitrary string without any colon ':'. + + @param str A string to validate + @param len Length of the string + @retval false The string is valid + @retval true The string is invalid +*/ +static bool sysvar_validate_redirect_url(sys_var *self, THD *thd, + set_var *var) +{ + /* NULL is invalid. */ + if (check_not_null(self, thd, var)) + return true; + char *str= var->save_result.string_value.str; + size_t len= var->save_result.string_value.length; + LEX_CSTRING mysql_prefix= {STRING_WITH_LEN("mysql://")}; + LEX_CSTRING maria_prefix= {STRING_WITH_LEN("mariadb://")}; + /* Empty string is valid */ + if (len == 0) + return false; + const char* end= str + len; + if (!strncmp(str, mysql_prefix.str, mysql_prefix.length)) + str+= mysql_prefix.length; + else if (!strncmp(str, maria_prefix.str, maria_prefix.length)) + str+= maria_prefix.length; + else + return true; + /* Host name cannot be empty */ + if (str == end) + return true; + /* Find the colon, if any */ + while (str < end && *str != ':') + str++; + /* Found colon */ + if (str < end) + { + /* Should have at least one number after the colon */ + if (str + 1 == end) + return true; + int p= 0; + while (str < end && isdigit(*++str)) + if ((p= p * 10 + (*str - '0')) > 65535) + return true; + /* Should be all numbers after the colon */ + if (str < end) + return true; + } + return false; +} + +static Sys_var_charptr Sys_redirect_url( + "redirect_url", + "URL of another server to redirect clients to. " + "Empty string means no redirection", + SESSION_VAR(redirect_url), CMD_LINE(REQUIRED_ARG), DEFAULT(""), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(sysvar_validate_redirect_url)); static Sys_var_sesvartrack Sys_track_session_sys_vars( "session_track_system_variables", "Track changes in registered system variables. ", CMD_LINE(REQUIRED_ARG), DEFAULT("autocommit,character_set_client,character_set_connection," - "character_set_results,time_zone")); + "character_set_results,redirect_url,time_zone")); static bool update_session_track_schema(sys_var *self, THD *thd, enum_var_type type) diff --git a/sql/sys_vars.inl b/sql/sys_vars.inl index e5e47215643..7b538c89f06 100644 --- a/sql/sys_vars.inl +++ b/sql/sys_vars.inl @@ -495,9 +495,10 @@ public: Note that the memory management for SESSION_VAR's is manual, the value must be strdup'ed in THD::init() and freed in - plugin_thdvar_cleanup(). TODO: it should be done automatically when - we'll have more session string variables to justify it. Maybe some - kind of a loop over all variables, like sys_var_end() in set_var.cc? + plugin_thdvar_cleanup(), see e.g. redirect_url. TODO: it should be + done automatically when we'll have more session string variables to + justify it. Maybe some kind of a loop over all variables, like + sys_var_end() in set_var.cc? */ class Sys_var_charptr: public sys_var {