diff --git a/mysql-test/r/set_thread_var.result b/mysql-test/r/set_thread_var.result new file mode 100644 index 000000000000..75b2e5cf3209 --- /dev/null +++ b/mysql-test/r/set_thread_var.result @@ -0,0 +1,42 @@ +CREATE USER mysqluser1; +SET max_join_size = 1000; +include/assert.inc [Check if max_join_size is correct] +SET max_join_size = 2000; +include/assert.inc [Check if max_join_size is correct] +SET SESSION $mysqluser1 max_join_size = 1001; +include/assert.inc [Check if max_join_size is correct] +SET SESSION $mysqluser1 innodb_lock_wait_timeout = 100; +SET SESSION $conn_root max_join_size = 2001; +include/assert.inc [Check if max_join_size is correct] +SET SESSION max_join_size = 2002; +include/assert.inc [Check if max_join_size is correct] +include/assert.inc [Check if max_join_size is correct] +include/assert.inc [Check if innodb_lock_wait_timeout is correct] +SET SESSION $conn_mysqluser1 max_join_size = 1002, SESSION $conn_root max_join_size = 2003; +include/assert.inc [Check if max_join_size is correct] +include/assert.inc [Check if max_join_size is correct] +SET SESSION $conn_root max_join_size = 1003, SESSION $conn_root max_join_size = 2004; +ERROR 42000: Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation +include/assert.inc [Check if max_join_size is correct] +SET SESSION $mysqluser1 max_join_size = 1003; +include/assert.inc [Check if max_join_size is correct] +SET SESSION max_join_size = 1004; +include/assert.inc [Check if max_join_size is correct] +include/assert.inc [Check if max_join_size is correct] +SET SESSION $conn_mysqluser1 max_join_size = 1005, max_sort_length = 30, SESSION $conn_root max_join_size = 2004, max_sort_length = 31; +include/assert.inc [Check if max_join_size is correct] +include/assert.inc [Check if max_join_size is correct] +SET SESSION 999 max_join_size = 9999; +ERROR HY000: Unknown thread id: 999 +SET GLOBAL 1 max_join_size = 1000; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1 max_join_size = 1000' at line 1 +SET SESSION -1 max_join_size = 1000; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '-1 max_join_size = 1000' at line 1 +SET SESSION 1 TRANSACTION READ WRITE; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'READ WRITE' at line 1 +SET GLOBAL TRANSACTION READ ONLY; +SET SESSION TRANSACTION READ ONLY; +include/assert.inc [Check if SET SESSION/GLOBAL TRANSACTION READ ONLY works correctly] +SET GLOBAL TRANSACTION READ WRITE; +SET SESSION TRANSACTION READ WRITE; +DROP USER mysqluser1; diff --git a/mysql-test/t/set_thread_var.test b/mysql-test/t/set_thread_var.test new file mode 100644 index 000000000000..4ebd8698939e --- /dev/null +++ b/mysql-test/t/set_thread_var.test @@ -0,0 +1,159 @@ +--source include/count_sessions.inc + +# +# test SET VARIABLES with thread id +# + +--let $conn_root = `SELECT CONNECTION_ID()` + +# Create a regular user +CREATE USER mysqluser1; + +--connect (user1,localhost,mysqluser1) +--let $conn_mysqluser1 = `SELECT CONNECTION_ID()` + +# Set a variable for current thd +SET max_join_size = 1000; + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 1000 +--source include/assert.inc + + +# Connect as root +--connection default + +# Set a variable for current thd +SET max_join_size = 2000; + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 2000 +--source include/assert.inc + +# Set variable for user1 thd from root thd +--replace_regex /SESSION [0-9]*/SESSION $mysqluser1/ +--eval SET SESSION $conn_mysqluser1 max_join_size = 1001 + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 2000 +--source include/assert.inc + +--replace_regex /SESSION [0-9]*/SESSION $mysqluser1/ +--eval SET SESSION $conn_mysqluser1 innodb_lock_wait_timeout = 100 + +# Set variable for root thd from root thd +--replace_regex /SESSION [0-9]*/SESSION $conn_root/ +--eval SET SESSION $conn_root max_join_size = 2001 + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 2001 +--source include/assert.inc + +# Set variable for root thd from root thd without session ID +SET SESSION max_join_size = 2002; + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 2002 +--source include/assert.inc + + +--connection user1 + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 1001 +--source include/assert.inc + +--let $assert_text = Check if innodb_lock_wait_timeout is correct +--let $assert_cond = @@innodb_lock_wait_timeout = 100 +--source include/assert.inc + + +# Reconnect as root +--connection default + +# Set variable for both current thd and user1 from root thd +--replace_regex /SESSION [0-9]* max_join_size = 1002, SESSION [0-9]* max_join_size = 2003/SESSION $conn_mysqluser1 max_join_size = 1002, SESSION $conn_root max_join_size = 2003/ +--eval SET SESSION $conn_mysqluser1 max_join_size = 1002, SESSION $conn_root max_join_size = 2003 + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 2003 +--source include/assert.inc + + +--connection user1 + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 1002 +--source include/assert.inc + +# Set variable for root thd from user1 thd +--replace_regex /SESSION [0-9]*/SESSION $conn_root/ /thread [0-9]*/thread $root/ +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +--eval SET SESSION $conn_mysqluser1 max_join_size = 1003, SESSION $conn_root max_join_size = 2004 + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 1002 +--source include/assert.inc + +# Set variable for user1 thd from user1 thd +--replace_regex /SESSION [0-9]*/SESSION $mysqluser1/ +--eval SET SESSION $conn_mysqluser1 max_join_size = 1003 + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 1003 +--source include/assert.inc + +# Set variable for user1 thd from user1 thd without session ID +SET SESSION max_join_size = 1004; + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 1004 +--source include/assert.inc + + +--connection default + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 2003 +--source include/assert.inc + +# Set multiple variables for both current thd and user1 from root thd +--replace_regex /SESSION [0-9]* max_join_size = 1005/SESSION $conn_mysqluser1 max_join_size = 1005/ /SESSION [0-9]* max_join_size = 2004/SESSION $conn_root max_join_size = 2004/ +--eval SET SESSION $conn_mysqluser1 max_join_size = 1005, max_sort_length = 30, SESSION $conn_root max_join_size = 2004, max_sort_length = 31 + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 2004 AND @@max_sort_length = 31 +--source include/assert.inc + + +--connection user1 + +--let $assert_text = Check if max_join_size is correct +--let $assert_cond = @@max_join_size = 1005 AND @@max_sort_length = 30 +--source include/assert.inc + +--error ER_NO_SUCH_THREAD +--eval SET SESSION 999 max_join_size = 9999 + +--error ER_PARSE_ERROR +--eval SET GLOBAL 1 max_join_size = 1000 + +--error ER_PARSE_ERROR +--eval SET SESSION -1 max_join_size = 1000 + +--error ER_PARSE_ERROR +--eval SET SESSION 1 TRANSACTION READ WRITE + +--connection default +SET GLOBAL TRANSACTION READ ONLY; +SET SESSION TRANSACTION READ ONLY; +--let $assert_text = Check if SET SESSION/GLOBAL TRANSACTION READ ONLY works correctly +--let $assert_cond = @@GLOBAL.transaction_read_only = 1 AND @@transaction_read_only = 1 +--source include/assert.inc +SET GLOBAL TRANSACTION READ WRITE; +SET SESSION TRANSACTION READ WRITE; + +# cleanup +DROP USER mysqluser1; +--disconnect user1 +--source include/wait_until_count_sessions.inc diff --git a/sql/parse_tree_helpers.cc b/sql/parse_tree_helpers.cc index ddb1beae6937..508976c8df54 100644 --- a/sql/parse_tree_helpers.cc +++ b/sql/parse_tree_helpers.cc @@ -263,11 +263,13 @@ bool sp_create_assignment_instr(THD *thd, const char *expr_end_ptr) { /* Remember option_type of the currently parsed LEX. */ const enum_var_type inner_option_type = lex->option_type; + const ulong thd_id_opt = lex->thread_id_opt; if (sp->restore_lex(thd)) return true; /* Copy option_type to outer lex in case it has changed. */ thd->lex->option_type = inner_option_type; + thd->lex->thread_id_opt = thd_id_opt; return false; } diff --git a/sql/parse_tree_nodes.cc b/sql/parse_tree_nodes.cc index bef204d81ad3..760b0f169db8 100644 --- a/sql/parse_tree_nodes.cc +++ b/sql/parse_tree_nodes.cc @@ -517,6 +517,7 @@ static bool add_system_variable_assignment(THD *thd, LEX_CSTRING prefix, set_var *var = new (thd->mem_root) set_var(var_type, var_tracker, val); if (var == nullptr) return true; + var->thd_id = lex->thread_id_opt; return lex->var_list.push_back(var); } @@ -4133,8 +4134,9 @@ bool PT_set_system_variable::do_contextualize(Parse_context *pc) { } bool PT_option_value_type::do_contextualize(Parse_context *pc) { - pc->thd->lex->option_type = type; - return super::do_contextualize(pc) || value->contextualize(pc); + pc->thd->lex->option_type = option_type; + pc->thd->lex->thread_id_opt = thread_id; + return Parse_tree_node::do_contextualize(pc) || value->contextualize(pc); } bool PT_option_value_list_head::do_contextualize(Parse_context *pc) { @@ -4206,6 +4208,8 @@ bool PT_start_option_value_list_transaction::do_contextualize( bool PT_start_option_value_list_following_option_type_eq::do_contextualize( Parse_context *pc) { + pc->thd->lex->option_type = option_type; + pc->thd->lex->thread_id_opt = thread_id; if (super::do_contextualize(pc) || head->contextualize(pc)) return true; if (sp_create_assignment_instr(pc->thd, head_pos.raw.end)) return true; @@ -4219,6 +4223,7 @@ bool PT_start_option_value_list_following_option_type_eq::do_contextualize( bool PT_start_option_value_list_following_option_type_transaction:: do_contextualize(Parse_context *pc) { + pc->thd->lex->option_type = type; if (super::do_contextualize(pc) || characteristics->contextualize(pc)) return true; @@ -4231,7 +4236,6 @@ bool PT_start_option_value_list_following_option_type_transaction:: } bool PT_start_option_value_list_type::do_contextualize(Parse_context *pc) { - pc->thd->lex->option_type = type; return super::do_contextualize(pc) || list->contextualize(pc); } diff --git a/sql/parse_tree_nodes.h b/sql/parse_tree_nodes.h index 1c146f176425..2188b063ac7d 100644 --- a/sql/parse_tree_nodes.h +++ b/sql/parse_tree_nodes.h @@ -1047,13 +1047,18 @@ class PT_option_value_no_option_type_password_for class PT_option_value_type : public Parse_tree_node { typedef Parse_tree_node super; - enum_var_type type; + enum_var_type option_type; + ulong thread_id; PT_set_scoped_system_variable *value; public: - PT_option_value_type(const POS &pos, enum_var_type type_arg, + PT_option_value_type(const POS &pos, enum_var_type option_type_arg, + ulong thread_id_arg, PT_set_scoped_system_variable *value_arg) - : super(pos), type(type_arg), value(value_arg) {} + : super(pos), + option_type(option_type_arg), + thread_id(thread_id_arg), + value(value_arg) {} bool do_contextualize(Parse_context *pc) override; }; @@ -1189,15 +1194,20 @@ class PT_start_option_value_list_following_option_type_eq : public PT_start_option_value_list_following_option_type { typedef PT_start_option_value_list_following_option_type super; + enum_var_type option_type; + ulong thread_id; PT_set_scoped_system_variable *head; POS head_pos; PT_option_value_list_head *opt_tail; public: PT_start_option_value_list_following_option_type_eq( - const POS &pos, PT_set_scoped_system_variable *head_arg, - const POS &head_pos_arg, PT_option_value_list_head *opt_tail_arg) + const POS &pos, enum_var_type option_type_arg, ulong thread_id_arg, + PT_set_scoped_system_variable *head_arg, const POS &head_pos_arg, + PT_option_value_list_head *opt_tail_arg) : super(pos), + option_type(option_type_arg), + thread_id(thread_id_arg), head(head_arg), head_pos(head_pos_arg), opt_tail(opt_tail_arg) {} @@ -1209,14 +1219,17 @@ class PT_start_option_value_list_following_option_type_transaction : public PT_start_option_value_list_following_option_type { typedef PT_start_option_value_list_following_option_type super; + enum_var_type type; PT_transaction_characteristics *characteristics; POS characteristics_pos; public: PT_start_option_value_list_following_option_type_transaction( - const POS &pos, PT_transaction_characteristics *characteristics_arg, + const POS &pos, enum_var_type type_arg, + PT_transaction_characteristics *characteristics_arg, const POS &characteristics_pos_arg) : super(pos), + type(type_arg), characteristics(characteristics_arg), characteristics_pos(characteristics_pos_arg) {} @@ -1226,14 +1239,13 @@ class PT_start_option_value_list_following_option_type_transaction class PT_start_option_value_list_type : public PT_start_option_value_list { typedef PT_start_option_value_list super; - enum_var_type type; PT_start_option_value_list_following_option_type *list; public: PT_start_option_value_list_type( - const POS &pos, enum_var_type type_arg, + const POS &pos, PT_start_option_value_list_following_option_type *list_arg) - : super(pos), type(type_arg), list(list_arg) {} + : super(pos), list(list_arg) {} bool do_contextualize(Parse_context *pc) override; }; diff --git a/sql/set_var.cc b/sql/set_var.cc index 24d9cfca6a76..b5036cdf34b6 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -56,7 +56,8 @@ #include "sql/item.h" #include "sql/item_func.h" #include "sql/log.h" -#include "sql/mysqld.h" // system_charset_info +#include "sql/mysqld.h" // system_charset_info +#include "sql/mysqld_thd_manager.h" // Global_THD_manager #include "sql/persisted_variable.h" #include "sql/protocol_classic.h" #include "sql/session_tracker.h" @@ -81,6 +82,7 @@ using std::min; using std::string; +using set_var_list_map = std::unordered_map>; static collation_unordered_map *static_system_variable_hash; @@ -1405,6 +1407,64 @@ sys_var *check_find_sys_var(THD *thd, const char *str, size_t length, return var; } +/** + Helper function to set variables. + Use the thd -> list map to iterate over all threads + For each thread, for each variable in its list, perform the specified function + @param thd Current thread + @param thd_var_map Map of threads to list of variables to be updated + @param func Function to be executed for each variable + @retval + 0 ok + @retval + 1 ERROR +*/ + +static int process_set_variable_map(THD *thd, set_var_list_map &thd_var_map, + int (*func)(THD *, set_var_base *)) { + DBUG_ENTER("process_set_variable_map"); + int error = 0; + + for (auto var_iter : thd_var_map) { + ulong thd_id = var_iter.first; + // Thread id of the current thread is 0 + if (!thd_id) { + List_iterator_fast it(var_iter.second); + set_var_base *var; + while ((var = it++)) { + if ((error = func(thd, var))) break; + } + } else { + Find_thd_with_id find_thd_with_id(thd_id); + THD_ptr thd_ptr = + Global_THD_manager::get_instance()->find_thd(&find_thd_with_id); + if (!thd_ptr) { + my_error(ER_NO_SUCH_THREAD, MYF(0), thd_id); + DBUG_RETURN(1); + } + + Security_context *sctx = thd->security_context(); + if (!sctx->check_access(SUPER_ACL) && + !sctx->has_global_grant(STRING_WITH_LEN("SYSTEM_VARIABLES_ADMIN")) + .first) { + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), + "SUPER or SYSTEM_VARIABLES_ADMIN"); + DBUG_RETURN(1); + } + + List_iterator_fast it(var_iter.second); + set_var_base *var; + while ((var = it++)) { + if ((error = func(thd_ptr.get(), var))) break; + } + } + + if (error) break; + } + + DBUG_RETURN(error); +} + /** Execute update of all variables. @@ -1416,8 +1476,8 @@ sys_var *check_find_sys_var(THD *thd, const char *str, size_t length, @param thd Thread id @param var_list List of variables to update - @param opened True means tables are open and this function will lock - them. + @param opened True means tables are open and this function will + lock them. @retval 0 ok @@ -1436,27 +1496,51 @@ int sql_set_variables(THD *thd, List *var_list, bool opened) { LEX *lex = thd->lex; set_var_base *var; + const ulong current_thd_id = thd->thread_id(); + /* + This map stores a list of variables to be updated for each thread + specified by the user, essentially grouping variabled by thread ID. + Variable updates are processed per thread. + */ + set_var_list_map thd_var_map; + + while ((var = it++)) { + ulong thd_id_opt = var->thd_id; + if (thd_id_opt == current_thd_id) thd_id_opt = 0; + thd_var_map[thd_id_opt].push_back(var); + } + if (!thd->lex->unit->is_prepared()) { lex->set_using_hypergraph_optimizer( thd->optimizer_switch_flag(OPTIMIZER_SWITCH_HYPERGRAPH_OPTIMIZER)); const Prepared_stmt_arena_holder ps_arena_holder(thd); - while ((var = it++)) { - if ((error = var->resolve(thd))) goto err; - } + + if ((error = process_set_variable_map( + thd, thd_var_map, [](THD *each_thd, set_var_base *svar) { + return svar->resolve(each_thd); + }))) + goto err; + if ((error = thd->is_error())) goto err; thd->lex->unit->set_prepared(); if (!thd->stmt_arena->is_regular()) thd->lex->save_cmd_properties(thd); } + if (opened && lock_tables(thd, lex->query_tables, lex->table_count, 0)) { error = 1; goto err; } + thd->lex->set_exec_started(); + if ((error = process_set_variable_map(thd, thd_var_map, + [](THD *each_thd, set_var_base *svar) { + return svar->check(each_thd); + }))) + goto err; + it.rewind(); while ((var = it++)) { - if ((error = var->check(thd))) goto err; - set_var *setvar = dynamic_cast(var); if (setvar && (setvar->type == OPT_PERSIST || setvar->type == OPT_PERSIST_ONLY) && @@ -1465,34 +1549,33 @@ int sql_set_variables(THD *thd, List *var_list, bool opened) { There are certain variables that can process NULL as a default value TODO: there should be no exceptions! */ - static const std::set exceptions = { - "basedir", - "character_sets_dir", - "ft_stopword_file", - "lc_messages_dir", - "plugin_dir", - "relay_log", - "replica_load_tmpdir", - "socket", - "tmpdir", - "init_file", - "admin_ssl_ca", - "admin_ssl_capath", - "admin_ssl_cert", - "admin_ssl_cipher", - "admin_tls_ciphersuites", - "admin_ssl_key", - "admin_ssl_crl", - "admin_ssl_crlpath", - "ssl_ca", - "ssl_capath", - "ssl_cert", - "ssl_cipher", - "tls_ciphersuites", - "ssl_key", - "ssl_crl", - "ssl_crlpath", - "group_replication_recovery_tls_ciphersuites"}; + static const std::set exceptions = {"basedir", + "character_sets_dir", + "ft_stopword_file", + "lc_messages_dir", + "plugin_dir", + "relay_log", + "relay_log_info_file", + "replica_load_tmpdir", + "socket", + "tmpdir", + "init_file", + "admin_ssl_ca", + "admin_ssl_capath", + "admin_ssl_cert", + "admin_ssl_cipher", + "admin_tls_ciphersuites", + "admin_ssl_key", + "admin_ssl_crl", + "admin_ssl_crlpath", + "ssl_ca", + "ssl_capath", + "ssl_cert", + "ssl_cipher", + "tls_ciphersuites", + "ssl_key", + "ssl_crl", + "ssl_crlpath"}; if (setvar->value && setvar->value->is_null() && exceptions.find(setvar->m_var_tracker.get_var_name()) == @@ -1523,11 +1606,12 @@ int sql_set_variables(THD *thd, List *var_list, bool opened) { } if ((error = thd->is_error())) goto err; - it.rewind(); - while ((var = it++)) { - if ((error = var->update(thd))) // Returns 0, -1 or 1 - goto err; - } + if ((error = process_set_variable_map( + thd, thd_var_map, [](THD *each_thd, set_var_base *svar) { + return svar->update(each_thd); /* Returns 0, -1 or 1 */ + }))) + goto err; + if (!error) { /* At this point SET statement is considered a success. */ Persisted_variables_cache *pv = nullptr; diff --git a/sql/set_var.h b/sql/set_var.h index 78bedfa59c85..83d71de869c5 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -110,6 +110,7 @@ class sys_var { public: sys_var *next; LEX_CSTRING name; + ulong thd_id; /** If the variable has an alias in the persisted variables file, this should point to it. This has the following consequences: @@ -973,6 +974,8 @@ class set_var_base { /** Used to identify if variable is sensitive or not */ virtual bool is_sensitive() const { return false; } + + ulong thd_id = 0; }; /** diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index ae1a366e6a21..600aca6950a6 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -478,6 +478,7 @@ void LEX::reset() { m_extended_show = false; option_type = OPT_DEFAULT; check_opt = HA_CHECK_OPT(); + thread_id_opt = 0; clear_privileges(); grant_as.cleanup(); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 8b026c942498..280c5ccbe5d5 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -4191,6 +4191,7 @@ struct LEX : public Query_tables_list { bool contains_plaintext_password; enum_keep_diagnostics keep_diagnostics; uint32 next_binlog_file_nr; + ulong thread_id_opt; /* thread id option */ private: bool m_broken; ///< see mark_broken() diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 4c6862958b38..be4c21cc4e33 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -3141,8 +3141,6 @@ static void plugin_vars_free_values(sys_var *vars) { void plugin_thdvar_safe_update(THD *thd, SYS_VAR *var, char **dest, const char *value) { - assert(thd == current_thd); - if (var->flags & PLUGIN_VAR_THDLOCAL) { if ((var->flags & PLUGIN_VAR_TYPEMASK) == PLUGIN_VAR_STR && var->flags & PLUGIN_VAR_MEMALLOC) diff --git a/sql/sql_plugin_var.cc b/sql/sql_plugin_var.cc index 8b16e64dca9c..b89e7fe577d8 100644 --- a/sql/sql_plugin_var.cc +++ b/sql/sql_plugin_var.cc @@ -378,7 +378,6 @@ bool sys_var_pluginvar::session_update(THD *thd, set_var *var) { bool rc = false; assert(!is_readonly()); assert(plugin_var->flags & PLUGIN_VAR_THDLOCAL); - assert(thd == current_thd); mysql_mutex_lock(&LOCK_global_system_variables); void *tgt = real_value_ptr(thd, var->type); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index aa2607ee5e10..3e1c2e2480a7 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1604,6 +1604,8 @@ void warn_on_deprecated_user_defined_collation( factor opt_source_count opt_load_parallel + opt_var_tid + option_type_with_thread_id %type ulonglong_num real_ulonglong_num size_number @@ -16199,9 +16201,9 @@ start_option_value_list: { $$= NEW_PTN PT_start_option_value_list_transaction(@$, $2, @2); } - | option_type start_option_value_list_following_option_type + | start_option_value_list_following_option_type { - $$= NEW_PTN PT_start_option_value_list_type(@$, $1, $2); + $$= NEW_PTN PT_start_option_value_list_type(@$, $1); } | PASSWORD equal TEXT_STRING_password opt_replace_password opt_retain_current_password { @@ -16311,18 +16313,20 @@ thread_id_list_options: // Start of option value list, option_type was given start_option_value_list_following_option_type: - option_value_following_option_type option_value_list_continued + option_type_with_thread_id option_value_following_option_type option_value_list_continued { - $$= - NEW_PTN PT_start_option_value_list_following_option_type_eq(@$, $1, - @1, - $2); + $$= NEW_PTN + PT_start_option_value_list_following_option_type_eq(@$, OPT_SESSION, $1, $2, @2, $3); } - | TRANSACTION_SYM transaction_characteristics + | option_type option_value_following_option_type option_value_list_continued + { + $$= NEW_PTN + PT_start_option_value_list_following_option_type_eq(@$, $1, 0, $2, @2, $3); + } + | option_type TRANSACTION_SYM transaction_characteristics { $$= NEW_PTN - PT_start_option_value_list_following_option_type_transaction(@$, $2, - @2); + PT_start_option_value_list_following_option_type_transaction(@$, $1, $3, @3); } ; @@ -16346,9 +16350,13 @@ option_value_list: // Wrapper around option values following the first option value in the stmt. option_value: - option_type option_value_following_option_type + option_type_with_thread_id option_value_following_option_type + { + $$= NEW_PTN PT_option_value_type(@$, OPT_SESSION, $1, $2); + } + | option_type option_value_following_option_type { - $$= NEW_PTN PT_option_value_type(@$, $1, $2); + $$= NEW_PTN PT_option_value_type(@$, $1, 0, $2); } | option_value_no_option_type { $$= $1; } ; @@ -16361,6 +16369,20 @@ option_type: | SESSION_SYM { $$=OPT_SESSION; } ; +opt_var_tid: + ulong_num + { + if ($1 <= 0) + MYSQL_YYABORT; + $$=$1; + } + ; + +option_type_with_thread_id: + LOCAL_SYM opt_var_tid { $$= $2; } + | SESSION_SYM opt_var_tid { $$= $2; } + ; + opt_var_type: %empty { $$=OPT_SESSION; } | GLOBAL_SYM { $$=OPT_GLOBAL; }