diff options
author | Oleksandr Byelkin <sanja@mariadb.com> | 2016-05-30 21:22:50 +0200 |
---|---|---|
committer | Oleksandr Byelkin <sanja@mariadb.com> | 2016-08-31 17:17:46 +0200 |
commit | 0ee3e64c55664332e8e92eda55b43692159fe4fe (patch) | |
tree | b12a876260d383a9ded16593bda2edcc668cbf7c | |
parent | c8948b0d0db4c182a744bc8bdbde7cbccff3d57d (diff) | |
download | mariadb-git-0ee3e64c55664332e8e92eda55b43692159fe4fe.tar.gz |
MDEV-8931: (server part of) session state tracking
Transaction tracker
32 files changed, 1169 insertions, 266 deletions
diff --git a/include/mysql.h.pp b/include/mysql.h.pp index 57ce4f78d99..0ef3403626c 100644 --- a/include/mysql.h.pp +++ b/include/mysql.h.pp @@ -94,7 +94,8 @@ enum enum_session_state_type SESSION_TRACK_STATE_CHANGE, SESSION_TRACK_GTIDS, SESSION_TRACK_TRANSACTION_CHARACTERISTICS, - SESSION_TRACK_TRANSACTION_STATE + SESSION_TRACK_TRANSACTION_STATE, + SESSION_TRACK_END }; my_bool my_net_init(NET *net, Vio* vio, void *thd, unsigned int my_flags); void my_net_local_init(NET *net); diff --git a/include/mysql_com.h b/include/mysql_com.h index 7433411f29a..16af9849759 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -550,9 +550,6 @@ enum enum_mysql_set_option /* Type of state change information that the server can include in the Ok packet. - Note : 1) session_state_type shouldn't go past 255 (i.e. 1-byte boundary). - 2) Modify the definition of SESSION_TRACK_END when a new member is - added. */ enum enum_session_state_type { @@ -561,15 +558,14 @@ enum enum_session_state_type SESSION_TRACK_STATE_CHANGE, /* track session state changes */ SESSION_TRACK_GTIDS, SESSION_TRACK_TRANSACTION_CHARACTERISTICS, /* Transaction chistics */ - SESSION_TRACK_TRANSACTION_STATE /* Transaction state */ + SESSION_TRACK_TRANSACTION_STATE, /* Transaction state */ + SESSION_TRACK_END /* must be last */ }; #define SESSION_TRACK_BEGIN SESSION_TRACK_SYSTEM_VARIABLES -#define SESSION_TRACK_END SESSION_TRACK_TRANSACTION_STATE - #define IS_SESSION_STATE_TYPE(T) \ - (((int)(T) >= SESSION_TRACK_BEGIN) && ((T) <= SESSION_TRACK_END)) + (((int)(T) >= SESSION_TRACK_BEGIN) && ((T) < SESSION_TRACK_END)) #define net_new_transaction(net) ((net)->pkt_nr=0) diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 8c01fc8b9b6..aeee99ebe4d 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -1172,8 +1172,8 @@ bool net_send_ok(THD *thd, uint server_status, uint statement_warn_count, ulonglong affected_rows, ulonglong id, const char *message, - bool unused1 __attribute__((unused)), - bool unused2 __attribute__((unused))) + bool unused1, + bool unused2) { DBUG_ENTER("emb_net_send_ok"); MYSQL_DATA *data; diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index 7fc9fedd456..176e8186798 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -904,12 +904,21 @@ The following options may be given as the first argument: --server-id=# Uniquely identifies the server instance in the community of replication partners --session-track-schema - Track changes to the 'default schema'. + Track changes to the default schema. (Defaults to on; use --skip-session-track-schema to disable.) --session-track-state-change - Track changes to the 'session state'. + Track changes to the session state. --session-track-system-variables=name Track changes in registered system variables. + --session-track-transaction-info=name + Track changes to the transaction attributes. OFF to + disable; STATE to track just transaction state (Is there + an active transaction? Does it have any data? etc.); + CHARACTERISTICS to track transaction state and report all + statements needed to start a transaction withthe same + characteristics (isolation level, read only/read + write,snapshot - but not any work done / data modified + within the transaction). --show-slave-auth-info Show user and password in SHOW SLAVE HOSTS on this master. @@ -1395,6 +1404,7 @@ 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-transaction-info OFF show-slave-auth-info FALSE silent-startup FALSE skip-grant-tables TRUE 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 e451a22d322..78ca8ca4ad1 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 @@ -17,12 +17,14 @@ VARIABLE_NAME VARIABLE_VALUE SESSION_TRACK_SCHEMA ON SESSION_TRACK_STATE_CHANGE OFF SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SESSION_TRACK_TRANSACTION_INFO OFF # via INFORMATION_SCHEMA.SESSION_VARIABLES SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; VARIABLE_NAME VARIABLE_VALUE SESSION_TRACK_SCHEMA ON SESSION_TRACK_STATE_CHANGE OFF SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SESSION_TRACK_TRANSACTION_INFO OFF SET @global_saved_tmp = @@global.session_track_system_variables; # Altering global variable's value diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index 13733c38a94..eecebce80ad 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -3327,34 +3327,6 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED -VARIABLE_NAME SESSION_TRACK_SCHEMA -SESSION_VALUE ON -GLOBAL_VALUE ON -GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE ON -VARIABLE_SCOPE SESSION -VARIABLE_TYPE BOOLEAN -VARIABLE_COMMENT Track changes to the 'default schema'. -NUMERIC_MIN_VALUE NULL -NUMERIC_MAX_VALUE NULL -NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST OFF,ON -READ_ONLY NO -COMMAND_LINE_ARGUMENT OPTIONAL -VARIABLE_NAME SESSION_TRACK_STATE_CHANGE -SESSION_VALUE OFF -GLOBAL_VALUE OFF -GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE OFF -VARIABLE_SCOPE SESSION -VARIABLE_TYPE BOOLEAN -VARIABLE_COMMENT Track changes to the 'session state'. -NUMERIC_MIN_VALUE NULL -NUMERIC_MAX_VALUE NULL -NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST OFF,ON -READ_ONLY NO -COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME SKIP_EXTERNAL_LOCKING SESSION_VALUE NULL GLOBAL_VALUE ON 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 c0d07280253..3a73ca07402 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -3796,7 +3796,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE ON VARIABLE_SCOPE SESSION VARIABLE_TYPE BOOLEAN -VARIABLE_COMMENT Track changes to the 'default schema'. +VARIABLE_COMMENT Track changes to the default schema. NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL @@ -3810,7 +3810,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE OFF VARIABLE_SCOPE SESSION VARIABLE_TYPE BOOLEAN -VARIABLE_COMMENT Track changes to the 'session state'. +VARIABLE_COMMENT Track changes to the session state. NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL @@ -3831,6 +3831,20 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME SESSION_TRACK_TRANSACTION_INFO +SESSION_VALUE OFF +GLOBAL_VALUE OFF +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE OFF +VARIABLE_SCOPE SESSION +VARIABLE_TYPE ENUM +VARIABLE_COMMENT Track changes to the transaction attributes. OFF to disable; STATE to track just transaction state (Is there an active transaction? Does it have any data? etc.); CHARACTERISTICS to track transaction state and report all statements needed to start a transaction withthe same characteristics (isolation level, read only/read write,snapshot - but not any work done / data modified within the transaction). +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST OFF,STATE,CHARACTERISTICS +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME SKIP_EXTERNAL_LOCKING SESSION_VALUE NULL GLOBAL_VALUE ON diff --git a/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test b/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test index bbb32bb67a3..90e6052947c 100644 --- a/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test +++ b/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test @@ -12,14 +12,10 @@ SELECT @@session.session_track_system_variables; --echo --echo # via INFORMATION_SCHEMA.GLOBAL_VARIABLES ---disable_warnings SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; ---enable_warnings --echo # via INFORMATION_SCHEMA.SESSION_VARIABLES ---disable_warnings SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; ---enable_warnings # Save the global value to be used to restore the original value. SET @global_saved_tmp = @@global.session_track_system_variables; diff --git a/sql/lock.cc b/sql/lock.cc index 2e44786d6fe..07286324fc5 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -90,6 +90,7 @@ extern HASH open_cache; static int lock_external(THD *thd, TABLE **table,uint count); static int unlock_external(THD *thd, TABLE **table,uint count); + /* Map the return value of thr_lock to an error from errmsg.txt */ static int thr_lock_errno_to_mysql[]= { 0, ER_LOCK_ABORTED, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; @@ -244,6 +245,39 @@ void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock) /** + Scan array of tables for access types; update transaction tracker + accordingly. + + @param thd The current thread. + @param tables An array of pointers to the tables to lock. + @param count The number of tables to lock. +*/ + +#ifndef EMBEDDED_LIBRARY +static void track_table_access(THD *thd, TABLE **tables, size_t count) +{ + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + { + Transaction_state_tracker *tst= (Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER); + + while (count--) + { + TABLE *t= tables[count]; + + if (t) + tst->add_trx_state(thd, t->reginfo.lock_type, + t->file->has_transactions()); + } + } +} +#else +#define track_table_access(A,B,C) +#endif //EMBEDDED_LIBRARY + + + +/** Lock tables. @param thd The current thread. @@ -280,6 +314,9 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) my_free(sql_lock); sql_lock= 0; } + + track_table_access(thd, tables, count); + DBUG_RETURN(sql_lock); } diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b59c6c7048f..28e91e208e7 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5285,16 +5285,16 @@ static int init_server_components() } plugins_are_initialized= TRUE; /* Don't separate from init function */ +#ifndef EMBEDDED_LIBRARY { - Session_tracker session_track_system_variables_check; - if (session_track_system_variables_check. - server_boot_verify(system_charset_info)) + if (Session_tracker::server_boot_verify(system_charset_info)) { sql_print_error("The variable session_track_system_variables has " "invalid values."); unireg_abort(1); } } +#endif //EMBEDDED_LIBRARY /* we do want to exit if there are any other unknown options */ if (remaining_argc > 1) diff --git a/sql/mysqld.h b/sql/mysqld.h index 68eab815564..846a01a9427 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -135,7 +135,6 @@ extern my_bool lower_case_file_system; extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs; extern my_bool opt_secure_auth; extern const char *current_dbug_option; -extern const char *current_session_track_system_variables; extern char* opt_secure_file_priv; extern char* opt_secure_backup_file_priv; extern size_t opt_secure_backup_file_priv_len; diff --git a/sql/protocol.cc b/sql/protocol.cc index 77dedfbc7d2..9ad9269f3b5 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -198,7 +198,7 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err, @param affected_rows Number of rows changed by statement @param id Auto_increment id for first row (if used) @param message Message to send to the client (Used by mysql_status) - @param is_eof this called inted of old EOF packet + @param is_eof this called instead of old EOF packet @return @retval FALSE The message was successfully sent @@ -217,10 +217,6 @@ net_send_ok(THD *thd, NET *net= &thd->net; StringBuffer<MYSQL_ERRMSG_SIZE + 10> store; - /* - To be used to manage the data storage in case session state change - information is present. - */ bool state_changed= false; bool error= FALSE; @@ -269,39 +265,25 @@ net_send_ok(THD *thd, } thd->get_stmt_da()->set_overwrite_status(true); - if ((thd->client_capabilities & CLIENT_SESSION_TRACK)) - { - if (server_status & SERVER_SESSION_STATE_CHANGED) - state_changed= true; - /* the info field */ - if (state_changed || (message && message[0])) - { - store.q_net_store_data((uchar*) message, message ? strlen(message) : 0); - } - - /* session state change information */ - if (unlikely(state_changed)) - { - store.set_charset(thd->variables.collation_database); + state_changed= + (thd->client_capabilities & CLIENT_SESSION_TRACK) && + (server_status & SERVER_SESSION_STATE_CHANGED); - thd->session_tracker.store(thd, &store); - } - } - else if (message && message[0]) + if (state_changed || (message && message[0])) { - /* the info field, if there is a message to store */ - DBUG_ASSERT(strlen(message) <= MYSQL_ERRMSG_SIZE); - store.q_net_store_data((uchar*) message, strlen(message)); + DBUG_ASSERT(safe_strlen(message) <= MYSQL_ERRMSG_SIZE); + store.q_net_store_data((uchar*) safe_str(message), safe_strlen(message)); } - if (store.length() > MAX_PACKET_LENGTH) + if (unlikely(state_changed)) { - net->error= 1; - net->last_errno= ER_NET_OK_PACKET_TOO_LARGE; - my_error(ER_NET_OK_PACKET_TOO_LARGE, MYF(0)); - DBUG_PRINT("info", ("OK packet too large")); - DBUG_RETURN(1); + store.set_charset(thd->variables.collation_database); + + thd->session_tracker.store(thd, &store); } + + DBUG_ASSERT(store.length() <= MAX_PACKET_LENGTH); + error= my_net_write(net, (const unsigned char*)store.ptr(), store.length()); if (!error && (!skip_flush || is_eof)) error= net_flush(net); diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc index cfbb1704318..bd641ab8d03 100644 --- a/sql/session_tracker.cc +++ b/sql/session_tracker.cc @@ -15,6 +15,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifndef EMBEDDED_LIBRARY #include "sql_plugin.h" #include "session_tracker.h" @@ -26,14 +27,20 @@ #include "sql_plugin.h" #include "set_var.h" +void State_tracker::mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name) +{ + m_changed= true; + thd->lex->safe_to_cache_query= 0; + thd->server_status|= SERVER_SESSION_STATE_CHANGED; +} + + class Not_implemented_tracker : public State_tracker { public: bool enable(THD *thd) { return false; } - bool check(THD *, set_var *) - { return false; } - bool update(THD *) + bool update(THD *, set_var *) { return false; } bool store(THD *, String *) { return false; } @@ -42,7 +49,6 @@ public: }; -static my_bool name_array_filler(void *ptr, void *data_ptr); /** Session_sysvars_tracker @@ -123,7 +129,7 @@ private: } } - uchar* search(sysvar_node_st *node, const sys_var *svar) + uchar* insert_or_search(sysvar_node_st *node, const sys_var *svar) { uchar *res; res= search(svar); @@ -146,7 +152,7 @@ private: void reset(); void copy(vars_list* from, THD *thd); bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error, - const CHARSET_INFO *char_set, bool session_created); + CHARSET_INFO *char_set, bool session_created); bool construct_var_list(char *buf, size_t buf_len); }; /** @@ -184,15 +190,13 @@ public: for session_track_system_variables during the server startup. */ - static bool server_init_check(THD *thd, const CHARSET_INFO *char_set, + static bool server_init_check(THD *thd, CHARSET_INFO *char_set, LEX_STRING var_list) { - vars_list dummy; - bool result; - result= dummy.parse_var_list(thd, var_list, false, char_set, false); - return result; + return check_var_list(thd, var_list, false, char_set, false); } - static bool server_init_process(THD *thd, const CHARSET_INFO *char_set, + + static bool server_init_process(THD *thd, CHARSET_INFO *char_set, LEX_STRING var_list) { vars_list dummy; @@ -205,16 +209,17 @@ public: void reset(); bool enable(THD *thd); - bool check(THD *thd, set_var *var); - bool check_str(THD *thd, LEX_STRING val); - bool update(THD *thd); + bool check_str(THD *thd, LEX_STRING *val); + bool update(THD *thd, set_var *var); bool store(THD *thd, String *buf); void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name); /* callback */ static uchar *sysvars_get_key(const char *entry, size_t *length, my_bool not_used __attribute__((unused))); - friend my_bool name_array_filler(void *ptr, void *data_ptr); + static my_bool name_array_filler(void *ptr, void *data_ptr); + static bool check_var_list(THD *thd, LEX_STRING var_list, bool throw_error, + CHARSET_INFO *char_set, bool session_created); }; @@ -240,12 +245,9 @@ public: } bool enable(THD *thd) - { return update(thd); } - bool check(THD *thd, set_var *var) - { return false; } - bool update(THD *thd); + { return update(thd, NULL); } + bool update(THD *thd, set_var *var); bool store(THD *thd, String *buf); - void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name); }; /* @@ -271,15 +273,10 @@ private: public: Session_state_change_tracker(); bool enable(THD *thd) - { return update(thd); }; - bool check(THD *thd, set_var *var) - { return false; } - bool update(THD *thd); + { return update(thd, NULL); }; + bool update(THD *thd, set_var *var); bool store(THD *thd, String *buf); - void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name); bool is_state_changed(THD*); - void ensure_enabled(THD *thd) - {} }; @@ -381,7 +378,7 @@ bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node, bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error, - const CHARSET_INFO *char_set, + CHARSET_INFO *char_set, bool session_created) { const char separator= ','; @@ -463,6 +460,80 @@ error: return true; } + +bool Session_sysvars_tracker::check_var_list(THD *thd, + LEX_STRING var_list, + bool throw_error, + CHARSET_INFO *char_set, + bool session_created) +{ + const char separator= ','; + char *token, *lasts= NULL; + size_t rest= var_list.length; + + if (!var_list.str || var_list.length == 0 || + !strcmp(var_list.str,(const char *)"*")) + { + return false; + } + + token= var_list.str; + + /* + If Lock to the plugin mutex is not acquired here itself, it results + in having to acquire it multiple times in find_sys_var_ex for each + token value. Hence the mutex is handled here to avoid a performance + overhead. + */ + if (!thd || session_created) + mysql_mutex_lock(&LOCK_plugin); + for (;;) + { + sys_var *svar; + LEX_STRING var; + + lasts= (char *) memchr(token, separator, rest); + + var.str= token; + if (lasts) + { + var.length= (lasts - token); + rest-= var.length + 1; + } + else + var.length= rest; + + /* Remove leading/trailing whitespace. */ + trim_whitespace(char_set, &var); + + if (!(svar= find_sys_var_ex(thd, var.str, var.length, throw_error, true))) + { + if (throw_error && session_created && thd) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "%.*s is not a valid system variable and will" + "be ignored.", (int)var.length, token); + } + else + { + if (!thd || session_created) + mysql_mutex_unlock(&LOCK_plugin); + return true; + } + } + + if (lasts) + token= lasts + 1; + else + break; + } + if (!thd || session_created) + mysql_mutex_unlock(&LOCK_plugin); + + return false; +} + struct name_array_filler_data { LEX_CSTRING **names; @@ -471,7 +542,8 @@ struct name_array_filler_data }; /** Collects variable references into array */ -static my_bool name_array_filler(void *ptr, void *data_ptr) +my_bool Session_sysvars_tracker::name_array_filler(void *ptr, + void *data_ptr) { Session_sysvars_tracker::sysvar_node_st *node= (Session_sysvars_tracker::sysvar_node_st *)ptr; @@ -578,18 +650,11 @@ bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf, bool Session_sysvars_tracker::enable(THD *thd) { - sys_var *svar; - mysql_mutex_lock(&LOCK_plugin); - svar= find_sys_var_ex(thd, SESSION_TRACK_SYSTEM_VARIABLES_NAME.str, - SESSION_TRACK_SYSTEM_VARIABLES_NAME.length, - false, true); - DBUG_ASSERT(svar); - - set_var tmp(thd, SHOW_OPT_GLOBAL, svar, &null_lex_str, NULL); - svar->session_save_default(thd, &tmp); - - if (tool_list->parse_var_list(thd, tmp.save_result.string_value, + LEX_STRING tmp; + tmp.str= global_system_variables.session_track_system_variables; + tmp.length= safe_strlen(tmp.str); + if (tool_list->parse_var_list(thd, tmp, true, thd->charset(), false) == true) { mysql_mutex_unlock(&LOCK_plugin); @@ -617,16 +682,10 @@ bool Session_sysvars_tracker::enable(THD *thd) @retval false Success */ -inline bool Session_sysvars_tracker::check(THD *thd, set_var *var) -{ - return check_str(thd, var->save_result.string_value); -} - -inline bool Session_sysvars_tracker::check_str(THD *thd, LEX_STRING val) +inline bool Session_sysvars_tracker::check_str(THD *thd, LEX_STRING *val) { - tool_list->reset(); - return tool_list->parse_var_list(thd, val, true, - thd->charset(), true); + return Session_sysvars_tracker::check_var_list(thd, *val, true, + thd->charset(), true); } @@ -645,8 +704,16 @@ inline bool Session_sysvars_tracker::check_str(THD *thd, LEX_STRING val) @retval false Success */ -bool Session_sysvars_tracker::update(THD *thd) +bool Session_sysvars_tracker::update(THD *thd, set_var *var) { + /* + We are doing via tool list because there possible errors with memory + in this case value will be unchanged. + */ + tool_list->reset(); + if (tool_list->parse_var_list(thd, var->save_result.string_value, true, + thd->charset(), true)) + return true; orig_list->copy(tool_list, thd); return false; } @@ -670,7 +737,7 @@ bool Session_sysvars_tracker::store(THD *thd, String *buf) SHOW_VAR show; const char *value; sysvar_node_st *node; - const CHARSET_INFO *charset; + CHARSET_INFO *charset; size_t val_length, length; int idx= 0; @@ -701,10 +768,15 @@ bool Session_sysvars_tracker::store(THD *thd, String *buf) val_length; compile_time_assert(SESSION_TRACK_SYSTEM_VARIABLES < 251); - buf->prep_alloc(1 + net_length_size(length) + length, EXTRA_ALLOC); + if (unlikely((1 + net_length_size(length) + length + buf->length() >= + MAX_PACKET_LENGTH) || + buf->prep_alloc(1 + net_length_size(length) + length, + EXTRA_ALLOC))) + return true; + /* Session state type (SESSION_TRACK_SYSTEM_VARIABLES) */ - buf->q_net_store_length((ulonglong)SESSION_TRACK_SYSTEM_VARIABLES); + buf->q_append((char)SESSION_TRACK_SYSTEM_VARIABLES); /* Length of the overall entity. */ buf->q_net_store_length((ulonglong)length); @@ -739,13 +811,10 @@ void Session_sysvars_tracker::mark_as_changed(THD *thd, Check if the specified system variable is being tracked, if so mark it as changed and also set the class's m_changed flag. */ - if ((node= (sysvar_node_st *) (orig_list->search(node, svar)))) + if ((node= (sysvar_node_st *) (orig_list->insert_or_search(node, svar)))) { node->m_changed= true; - m_changed= true; - /* do not cache the statement when there is change in session state */ - thd->lex->safe_to_cache_query= 0; - thd->server_status|= SERVER_SESSION_STATE_CHANGED; + State_tracker::mark_as_changed(thd, var); } } @@ -795,8 +864,6 @@ static Session_sysvars_tracker* sysvar_tracker(THD *thd) bool sysvartrack_validate_value(THD *thd, const char *str, size_t len) { LEX_STRING tmp= {(char *)str, len}; - if (thd && sysvar_tracker(thd)->is_enabled()) - return sysvar_tracker(thd)->check_str(thd, tmp); return Session_sysvars_tracker::server_init_check(thd, system_charset_info, tmp); } @@ -807,9 +874,9 @@ bool sysvartrack_reprint_value(THD *thd, char *str, size_t len) system_charset_info, tmp); } -bool sysvartrack_update(THD *thd) +bool sysvartrack_update(THD *thd, set_var *var) { - return sysvar_tracker(thd)->update(thd); + return sysvar_tracker(thd)->update(thd, var); } size_t sysvartrack_value_len(THD *thd) { @@ -831,7 +898,7 @@ bool sysvartrack_value_construct(THD *thd, char *val, size_t len) false (always) */ -bool Current_schema_tracker::update(THD *thd) +bool Current_schema_tracker::update(THD *thd, set_var *) { m_enabled= thd->variables.session_track_schema; return false; @@ -862,12 +929,13 @@ bool Current_schema_tracker::store(THD *thd, String *buf) compile_time_assert(SESSION_TRACK_SCHEMA < 251); compile_time_assert(NAME_LEN < 251); - DBUG_ASSERT(net_length_size(length) < 251); - if (buf->prep_alloc(1 + 1 + length, EXTRA_ALLOC)) + DBUG_ASSERT(length < 251); + if (unlikely((1 + 1 + length + buf->length() >= MAX_PACKET_LENGTH) || + buf->prep_alloc(1 + 1 + length, EXTRA_ALLOC))) return true; /* Session state type (SESSION_TRACK_SCHEMA) */ - buf->q_net_store_length((ulonglong)SESSION_TRACK_SCHEMA); + buf->q_append((char)SESSION_TRACK_SCHEMA); /* Length of the overall entity. */ buf->q_net_store_length(length); @@ -882,26 +950,522 @@ bool Current_schema_tracker::store(THD *thd, String *buf) /** - Mark the tracker as changed. + Reset the m_changed flag for next statement. + + @return void */ -void Current_schema_tracker::mark_as_changed(THD *thd, LEX_CSTRING *) +void Current_schema_tracker::reset() { - m_changed= true; - thd->lex->safe_to_cache_query= 0; - thd->server_status|= SERVER_SESSION_STATE_CHANGED; + m_changed= false; +} + + +/////////////////////////////////////////////////////////////////////////////// + + +Transaction_state_tracker::Transaction_state_tracker() +{ + m_enabled = false; + tx_changed = TX_CHG_NONE; + tx_curr_state = + tx_reported_state= TX_EMPTY; + tx_read_flags = TX_READ_INHERIT; + tx_isol_level = TX_ISOL_INHERIT; +} + +/** + Enable/disable the tracker based on @@session_track_transaction_info. + + @param thd [IN] The thd handle. + + @retval true if updating the tracking level failed + @retval false otherwise +*/ + +bool Transaction_state_tracker::update(THD *thd, set_var *) +{ + if (thd->variables.session_track_transaction_info != TX_TRACK_NONE) + { + /* + If we only just turned reporting on (rather than changing between + state and characteristics reporting), start from a defined state. + */ + if (!m_enabled) + { + tx_curr_state = + tx_reported_state = TX_EMPTY; + tx_changed |= TX_CHG_STATE; + m_enabled= true; + } + if (thd->variables.session_track_transaction_info == TX_TRACK_CHISTICS) + tx_changed |= TX_CHG_CHISTICS; + mark_as_changed(thd, NULL); + } + else + m_enabled= false; + + return false; +} + + +/** + Store the transaction state (and, optionally, characteristics) + as length-encoded string in the specified buffer. Once the data + is stored, we reset the flags related to state-change (see reset()). + + + @param thd [IN] The thd handle. + @paran buf [INOUT] Buffer to store the information to. + + @retval false Success + @retval true Error +*/ + +static LEX_CSTRING isol[]= { + { STRING_WITH_LEN("READ UNCOMMITTED") }, + { STRING_WITH_LEN("READ COMMITTED") }, + { STRING_WITH_LEN("REPEATABLE READ") }, + { STRING_WITH_LEN("SERIALIZABLE") } +}; + +bool Transaction_state_tracker::store(THD *thd, String *buf) +{ + /* STATE */ + if (tx_changed & TX_CHG_STATE) + { + uchar *to; + if (unlikely((11 + buf->length() >= MAX_PACKET_LENGTH) || + ((to= (uchar *) buf->prep_append(11, EXTRA_ALLOC)) == NULL))) + return true; + + *(to++)= (char)SESSION_TRACK_TRANSACTION_STATE; + + to= net_store_length((uchar *) to, (ulonglong) 9); + to= net_store_length((uchar *) to, (ulonglong) 8); + + *(to++)= (tx_curr_state & TX_EXPLICIT) ? 'T' : + ((tx_curr_state & TX_IMPLICIT) ? 'I' : '_'); + *(to++)= (tx_curr_state & TX_READ_UNSAFE) ? 'r' : '_'; + *(to++)= ((tx_curr_state & TX_READ_TRX) || + (tx_curr_state & TX_WITH_SNAPSHOT)) ? 'R' : '_'; + *(to++)= (tx_curr_state & TX_WRITE_UNSAFE) ? 'w' : '_'; + *(to++)= (tx_curr_state & TX_WRITE_TRX) ? 'W' : '_'; + *(to++)= (tx_curr_state & TX_STMT_UNSAFE) ? 's' : '_'; + *(to++)= (tx_curr_state & TX_RESULT_SET) ? 'S' : '_'; + *(to++)= (tx_curr_state & TX_LOCKED_TABLES) ? 'L' : '_'; + } + + /* CHARACTERISTICS -- How to restart the transaction */ + + if ((thd->variables.session_track_transaction_info == TX_TRACK_CHISTICS) && + (tx_changed & TX_CHG_CHISTICS)) + { + bool is_xa= (thd->transaction.xid_state.xa_state != XA_NOTR); + size_t start; + + /* 2 length by 1 byte and code */ + if (unlikely((1 + 1 + 1 + 110 + buf->length() >= MAX_PACKET_LENGTH) || + buf->prep_alloc(1 + 1 + 1, EXTRA_ALLOC))) + return true; + + compile_time_assert(SESSION_TRACK_TRANSACTION_CHARACTERISTICS < 251); + /* Session state type (SESSION_TRACK_TRANSACTION_CHARACTERISTICS) */ + buf->q_append((char)SESSION_TRACK_TRANSACTION_CHARACTERISTICS); + + /* placeholders for lengths. will be filled in at the end */ + buf->q_append('\0'); + buf->q_append('\0'); + + start= buf->length(); + + { + /* + We have four basic replay scenarios: + + a) SET TRANSACTION was used, but before an actual transaction + was started, the load balancer moves the connection elsewhere. + In that case, the same one-shots should be set up in the + target session. (read-only/read-write; isolation-level) + + b) The initial transaction has begun; the relevant characteristics + are the session defaults, possibly overridden by previous + SET TRANSACTION statements, possibly overridden or extended + by options passed to the START TRANSACTION statement. + If the load balancer wishes to move this transaction, + it needs to be replayed with the correct characteristics. + (read-only/read-write from SET or START; + isolation-level from SET only, snapshot from START only) + + c) A subsequent transaction started with START TRANSACTION + (which is legal syntax in lieu of COMMIT AND CHAIN in MySQL) + may add/modify the current one-shots: + + - It may set up a read-only/read-write one-shot. + This one-shot will override the value used in the previous + transaction (whether that came from the default or a one-shot), + and, like all one-shots currently do, it will carry over into + any subsequent transactions that don't explicitly override them + in turn. This behavior is not guaranteed in the docs and may + change in the future, but the tracker item should correctly + reflect whatever behavior a given version of mysqld implements. + + - It may also set up a WITH CONSISTENT SNAPSHOT one-shot. + This one-shot does not currently carry over into subsequent + transactions (meaning that with "traditional syntax", WITH + CONSISTENT SNAPSHOT can only be requested for the first part + of a transaction chain). Again, the tracker item should reflect + mysqld behavior. + + d) A subsequent transaction started using COMMIT AND CHAIN + (or, for that matter, BEGIN WORK, which is currently + legal and equivalent syntax in MySQL, or START TRANSACTION + sans options) will re-use any one-shots set up so far + (with SET before the first transaction started, and with + all subsequent STARTs), except for WITH CONSISTANT SNAPSHOT, + which will never be chained and only applies when explicitly + given. + + It bears noting that if we switch sessions in a follow-up + transaction, SET TRANSACTION would be illegal in the old + session (as a transaction is active), whereas in the target + session which is being prepared, it should be legal, as no + transaction (chain) should have started yet. + + Therefore, we are free to generate SET TRANSACTION as a replay + statement even for a transaction that isn't the first in an + ongoing chain. Consider + + SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED; + START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT; + # work + COMMIT AND CHAIN; + + If we switch away at this point, the replay in the new session + needs to be + + SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED; + START TRANSACTION READ ONLY; + + When a transaction ends (COMMIT/ROLLBACK sans CHAIN), all + per-transaction characteristics are reset to the session's + defaults. + + This also holds for a transaction ended implicitly! (transaction.cc) + Once again, the aim is to have the tracker item reflect on a + given mysqld's actual behavior. + */ + + /* + "ISOLATION LEVEL" + Only legal in SET TRANSACTION, so will always be replayed as such. + */ + if (tx_isol_level != TX_ISOL_INHERIT) + { + /* + Unfortunately, we can't re-use tx_isolation_names / + tx_isolation_typelib as it hyphenates its items. + */ + buf->append(STRING_WITH_LEN("SET TRANSACTION ISOLATION LEVEL ")); + buf->append(isol[tx_isol_level - 1].str, isol[tx_isol_level - 1].length); + buf->append(STRING_WITH_LEN("; ")); + } + + /* + Start transaction will usually result in TX_EXPLICIT (transaction + started, but no data attached yet), except when WITH CONSISTENT + SNAPSHOT, in which case we may have data pending. + If it's an XA transaction, we don't go through here so we can + first print the trx access mode ("SET TRANSACTION READ ...") + separately before adding XA START (whereas with START TRANSACTION, + we can merge the access mode into the same statement). + */ + if ((tx_curr_state & TX_EXPLICIT) && !is_xa) + { + buf->append(STRING_WITH_LEN("START TRANSACTION")); + + /* + "WITH CONSISTENT SNAPSHOT" + Defaults to no, can only be enabled. + Only appears in START TRANSACTION. + */ + if (tx_curr_state & TX_WITH_SNAPSHOT) + { + buf->append(STRING_WITH_LEN(" WITH CONSISTENT SNAPSHOT")); + if (tx_read_flags != TX_READ_INHERIT) + buf->append(STRING_WITH_LEN(",")); + } + + /* + "READ WRITE / READ ONLY" can be set globally, per-session, + or just for one transaction. + + The latter case can take the form of + START TRANSACTION READ (WRITE|ONLY), or of + SET TRANSACTION READ (ONLY|WRITE). + (Both set thd->read_only for the upcoming transaction; + it will ultimately be re-set to the session default.) + + As the regular session-variable tracker does not monitor the one-shot, + we'll have to do it here. + + If READ is flagged as set explicitly (rather than just inherited + from the session's default), we'll get the actual bool from the THD. + */ + if (tx_read_flags != TX_READ_INHERIT) + { + if (tx_read_flags == TX_READ_ONLY) + buf->append(STRING_WITH_LEN(" READ ONLY")); + else + buf->append(STRING_WITH_LEN(" READ WRITE")); + } + buf->append(STRING_WITH_LEN("; ")); + } + else if (tx_read_flags != TX_READ_INHERIT) + { + /* + "READ ONLY" / "READ WRITE" + We could transform this to SET TRANSACTION even when it occurs + in START TRANSACTION, but for now, we'll resysynthesize the original + command as closely as possible. + */ + buf->append(STRING_WITH_LEN("SET TRANSACTION ")); + if (tx_read_flags == TX_READ_ONLY) + buf->append(STRING_WITH_LEN("READ ONLY; ")); + else + buf->append(STRING_WITH_LEN("READ WRITE; ")); + } + + if ((tx_curr_state & TX_EXPLICIT) && is_xa) + { + XID *xid= &thd->transaction.xid_state.xid; + long glen, blen; + + buf->append(STRING_WITH_LEN("XA START")); + + if ((glen= xid->gtrid_length) > 0) + { + buf->append(STRING_WITH_LEN(" '")); + buf->append(xid->data, glen); + + if ((blen= xid->bqual_length) > 0) + { + buf->append(STRING_WITH_LEN("','")); + buf->append(xid->data + glen, blen); + } + buf->append(STRING_WITH_LEN("'")); + + if (xid->formatID != 1) + { + buf->append(STRING_WITH_LEN(",")); + buf->append_ulonglong(xid->formatID); + } + } + + buf->append(STRING_WITH_LEN("; ")); + } + + // discard trailing space + if (buf->length() > start) + buf->length(buf->length() - 1); + } + + { + ulonglong length= buf->length() - start; + uchar *place= (uchar *)(buf->ptr() + (start - 2)); + DBUG_ASSERT(length < 249); // in fact < 110 + DBUG_ASSERT(start >= 3); + + DBUG_ASSERT((place - 1)[0] == SESSION_TRACK_TRANSACTION_CHARACTERISTICS); + /* Length of the overall entity. */ + place[0]= length + 1; + /* Transaction characteristics (length-encoded string). */ + place[1]= length; + } + } + + reset(); + + return false; } /** Reset the m_changed flag for next statement. +*/ - @return void +void Transaction_state_tracker::reset() +{ + m_changed= false; + tx_reported_state= tx_curr_state; + tx_changed= TX_CHG_NONE; +} + + +/** + Helper function: turn table info into table access flag. + Accepts table lock type and engine type flag (transactional/ + non-transactional), and returns the corresponding access flag + out of TX_READ_TRX, TX_READ_UNSAFE, TX_WRITE_TRX, TX_WRITE_UNSAFE. + + @param thd [IN] The thd handle + @param set [IN] The table's access/lock type + @param set [IN] Whether the table's engine is transactional + + @return The table access flag */ -void Current_schema_tracker::reset() +enum_tx_state Transaction_state_tracker::calc_trx_state(THD *thd, + thr_lock_type l, + bool has_trx) { - m_changed= false; + enum_tx_state s; + bool read= (l <= TL_READ_NO_INSERT); + + if (read) + s= has_trx ? TX_READ_TRX : TX_READ_UNSAFE; + else + s= has_trx ? TX_WRITE_TRX : TX_WRITE_UNSAFE; + + return s; +} + + +/** + Register the end of an (implicit or explicit) transaction. + + @param thd [IN] The thd handle +*/ +void Transaction_state_tracker::end_trx(THD *thd) +{ + DBUG_ASSERT(thd->variables.session_track_transaction_info > TX_TRACK_NONE); + + if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) + return; + + if (tx_curr_state != TX_EMPTY) + { + if (tx_curr_state & TX_EXPLICIT) + tx_changed |= TX_CHG_CHISTICS; + tx_curr_state &= TX_LOCKED_TABLES; + } + update_change_flags(thd); +} + + +/** + Clear flags pertaining to the current statement or transaction. + May be called repeatedly within the same execution cycle. + + @param thd [IN] The thd handle. + @param set [IN] The flags to clear +*/ + +void Transaction_state_tracker::clear_trx_state(THD *thd, uint clear) +{ + if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) + return; + + tx_curr_state &= ~clear; + update_change_flags(thd); +} + + +/** + Add flags pertaining to the current statement or transaction. + May be called repeatedly within the same execution cycle, + e.g. to add access info for more tables. + + @param thd [IN] The thd handle. + @param set [IN] The flags to add +*/ + +void Transaction_state_tracker::add_trx_state(THD *thd, uint add) +{ + if ((!m_enabled) || (thd->state_flags & Open_tables_state::BACKUPS_AVAIL)) + return; + + if (add == TX_EXPLICIT) + { + /* Always send characteristic item (if tracked), always replace state. */ + tx_changed |= TX_CHG_CHISTICS; + tx_curr_state = TX_EXPLICIT; + } + + /* + If we're not in an implicit or explicit transaction, but + autocommit==0 and tables are accessed, we flag "implicit transaction." + */ + else if (!(tx_curr_state & (TX_EXPLICIT|TX_IMPLICIT)) && + (thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT) && + (add & + (TX_READ_TRX | TX_READ_UNSAFE | TX_WRITE_TRX | TX_WRITE_UNSAFE))) + tx_curr_state |= TX_IMPLICIT; + + /* + Only flag state when in transaction or LOCK TABLES is added. + */ + if ((tx_curr_state & (TX_EXPLICIT | TX_IMPLICIT)) || + (add & TX_LOCKED_TABLES)) + tx_curr_state |= add; + + update_change_flags(thd); +} + + +/** + Add "unsafe statement" flag if applicable. + + @param thd [IN] The thd handle. + @param set [IN] The flags to add +*/ + +void Transaction_state_tracker::add_trx_state_from_thd(THD *thd) +{ + if (m_enabled) + { + if (thd->lex->is_stmt_unsafe()) + add_trx_state(thd, TX_STMT_UNSAFE); + } +} + + +/** + Set read flags (read only/read write) pertaining to the next + transaction. + + @param thd [IN] The thd handle. + @param set [IN] The flags to set +*/ + +void Transaction_state_tracker::set_read_flags(THD *thd, + enum enum_tx_read_flags flags) +{ + if (m_enabled && (tx_read_flags != flags)) + { + tx_read_flags = flags; + tx_changed |= TX_CHG_CHISTICS; + mark_as_changed(thd, NULL); + } +} + + +/** + Set isolation level pertaining to the next transaction. + + @param thd [IN] The thd handle. + @param set [IN] The isolation level to set +*/ + +void Transaction_state_tracker::set_isol_level(THD *thd, + enum enum_tx_isol_level level) +{ + if (m_enabled && (tx_isol_level != level)) + { + tx_isol_level = level; + tx_changed |= TX_CHG_CHISTICS; + mark_as_changed(thd, NULL); + } } @@ -920,7 +1484,7 @@ Session_state_change_tracker::Session_state_change_tracker() **/ -bool Session_state_change_tracker::update(THD *thd) +bool Session_state_change_tracker::update(THD *thd, set_var *) { m_enabled= thd->variables.session_track_state_change; return false; @@ -938,12 +1502,13 @@ bool Session_state_change_tracker::update(THD *thd) bool Session_state_change_tracker::store(THD *thd, String *buf) { - if (buf->prep_alloc(1 + 1 + 1, EXTRA_ALLOC)) + if (unlikely((1 + 1 + 1 + buf->length() >= MAX_PACKET_LENGTH) || + buf->prep_alloc(1 + 1 + 1, EXTRA_ALLOC))) return true; compile_time_assert(SESSION_TRACK_STATE_CHANGE < 251); /* Session state type (SESSION_TRACK_STATE_CHANGE) */ - buf->q_net_store_length((ulonglong)SESSION_TRACK_STATE_CHANGE); + buf->q_append((char)SESSION_TRACK_STATE_CHANGE); /* Length of the overall entity (1 byte) */ buf->q_append('\1'); @@ -956,17 +1521,6 @@ bool Session_state_change_tracker::store(THD *thd, String *buf) return false; } -/** - Mark the tracker as changed and associated session - attributes accordingly. -*/ - -void Session_state_change_tracker::mark_as_changed(THD *thd, LEX_CSTRING *) -{ - m_changed= true; - thd->lex->safe_to_cache_query= 0; - thd->server_status|= SERVER_SESSION_STATE_CHANGED; -} /** Reset the m_changed flag for next statement. @@ -977,6 +1531,7 @@ void Session_state_change_tracker::reset() m_changed= false; } + /** Find if there is a session state change. */ @@ -994,7 +1549,12 @@ bool Session_state_change_tracker::is_state_changed(THD *) Session_tracker::Session_tracker() { - for (int i= 0; i <= SESSION_TRACKER_END; i ++) + /* track data ID fit into one byte in net coding */ + compile_time_assert(SESSION_TRACK_END < 251); + /* one tracker could serv several tracking data */ + compile_time_assert((uint)SESSION_TRACK_END >= (uint)SESSION_TRACKER_END); + + for (int i= 0; i < SESSION_TRACKER_END; i++) m_trackers[i]= NULL; } @@ -1024,9 +1584,9 @@ void Session_tracker::enable(THD *thd) m_trackers[SESSION_GTIDS_TRACKER]= new (std::nothrow) Not_implemented_tracker; m_trackers[TRANSACTION_INFO_TRACKER]= - new (std::nothrow) Not_implemented_tracker; + new (std::nothrow) Transaction_state_tracker; - for (int i= 0; i <= SESSION_TRACKER_END; i ++) + for (int i= 0; i < SESSION_TRACKER_END; i++) m_trackers[i]->enable(thd); } @@ -1039,20 +1599,14 @@ void Session_tracker::enable(THD *thd) @retval true Failure */ -bool Session_tracker::server_boot_verify(const CHARSET_INFO *char_set) +bool Session_tracker::server_boot_verify(CHARSET_INFO *char_set) { - Session_sysvars_tracker *server_tracker; bool result; - sys_var *svar= find_sys_var_ex(NULL, SESSION_TRACK_SYSTEM_VARIABLES_NAME.str, - SESSION_TRACK_SYSTEM_VARIABLES_NAME.length, - false, true); - DBUG_ASSERT(svar); - set_var tmp(NULL, SHOW_OPT_GLOBAL, svar, &null_lex_str, NULL); - svar->session_save_default(NULL, &tmp); - server_tracker= new (std::nothrow) Session_sysvars_tracker(); - result= server_tracker->server_init_check(NULL, char_set, - tmp.save_result.string_value); - delete server_tracker; + LEX_STRING tmp; + tmp.str= global_system_variables.session_track_system_variables; + tmp.length= safe_strlen(tmp.str); + result= + Session_sysvars_tracker::server_init_check(NULL, char_set, tmp); return result; } @@ -1067,7 +1621,6 @@ bool Session_tracker::server_boot_verify(const CHARSET_INFO *char_set) void Session_tracker::store(THD *thd, String *buf) { - /* Temporary buffer to store all the changes. */ size_t start; /* @@ -1079,7 +1632,7 @@ void Session_tracker::store(THD *thd, String *buf) start= buf->length(); /* Get total length. */ - for (int i= 0; i <= SESSION_TRACKER_END; i ++) + for (int i= 0; i < SESSION_TRACKER_END; i++) { if (m_trackers[i]->is_changed() && m_trackers[i]->store(thd, buf)) @@ -1105,3 +1658,5 @@ void Session_tracker::store(THD *thd, String *buf) net_store_length(data - 1, length); } + +#endif //EMBEDDED_LIBRARY diff --git a/sql/session_tracker.h b/sql/session_tracker.h index 7025c34967d..431726f03ed 100644 --- a/sql/session_tracker.h +++ b/sql/session_tracker.h @@ -20,6 +20,7 @@ #include "m_string.h" #include "thr_lock.h" +#ifndef EMBEDDED_LIBRARY /* forward declarations */ class THD; class set_var; @@ -32,12 +33,10 @@ enum enum_session_tracker CURRENT_SCHEMA_TRACKER, /* Current schema */ SESSION_STATE_CHANGE_TRACKER, SESSION_GTIDS_TRACKER, /* Tracks GTIDs */ - TRANSACTION_INFO_TRACKER /* Transaction state */ + TRANSACTION_INFO_TRACKER, /* Transaction state */ + SESSION_TRACKER_END /* must be the last */ }; -#define SESSION_TRACKER_END TRANSACTION_INFO_TRACKER - - /** State_tracker @@ -54,8 +53,7 @@ enum enum_session_tracker the respective system variable either through SET command or via command line option. As required in system variable handling, this interface also includes two functions to help in the verification of the supplied value - (ON_CHECK) and the updation (ON_UPDATE) of the tracker system variable, - namely - check() and update(). + (ON_UPDATE) of the tracker system variable, namely - update(). */ class State_tracker @@ -91,22 +89,19 @@ public: /** Called in the constructor of THD*/ virtual bool enable(THD *thd)= 0; - /** To be invoked when the tracker's system variable is checked (ON_CHECK). */ - virtual bool check(THD *thd, set_var *var)= 0; - /** To be invoked when the tracker's system variable is updated (ON_UPDATE).*/ - virtual bool update(THD *thd)= 0; + virtual bool update(THD *thd, set_var *var)= 0; /** Store changed data into the given buffer. */ virtual bool store(THD *thd, String *buf)= 0; /** Mark the entity as changed. */ - virtual void mark_as_changed(THD *thd, LEX_CSTRING *name)= 0; + virtual void mark_as_changed(THD *thd, LEX_CSTRING *name); }; bool sysvartrack_validate_value(THD *thd, const char *str, size_t len); bool sysvartrack_reprint_value(THD *thd, char *str, size_t len); -bool sysvartrack_update(THD *thd); +bool sysvartrack_update(THD *thd, set_var *var); size_t sysvartrack_value_len(THD *thd); bool sysvartrack_value_construct(THD *thd, char *val, size_t len); @@ -122,7 +117,7 @@ bool sysvartrack_value_construct(THD *thd, char *val, size_t len); class Session_tracker { private: - State_tracker *m_trackers[SESSION_TRACKER_END + 1]; + State_tracker *m_trackers[SESSION_TRACKER_END]; /* The following two functions are private to disable copying. */ Session_tracker(Session_tracker const &other) @@ -146,7 +141,7 @@ public: /* trick to make happy memory accounting system */ void deinit() { - for (int i= 0; i <= SESSION_TRACKER_END; i ++) + for (int i= 0; i < SESSION_TRACKER_END; i++) { if (m_trackers[i]) delete m_trackers[i]; @@ -155,7 +150,7 @@ public: } void enable(THD *thd); - bool server_boot_verify(const CHARSET_INFO *char_set); + static bool server_boot_verify(CHARSET_INFO *char_set); /** Returns the pointer to the tracker object for the specified tracker. */ inline State_tracker *get_tracker(enum_session_tracker tracker) const @@ -174,4 +169,136 @@ public: void store(THD *thd, String *main_buf); }; + +/* + Transaction_state_tracker +*/ + +/** + Transaction state (no transaction, transaction active, work attached, etc.) +*/ +enum enum_tx_state { + TX_EMPTY = 0, ///< "none of the below" + TX_EXPLICIT = 1, ///< an explicit transaction is active + TX_IMPLICIT = 2, ///< an implicit transaction is active + TX_READ_TRX = 4, ///< transactional reads were done + TX_READ_UNSAFE = 8, ///< non-transaction reads were done + TX_WRITE_TRX = 16, ///< transactional writes were done + TX_WRITE_UNSAFE = 32, ///< non-transactional writes were done + TX_STMT_UNSAFE = 64, ///< "unsafe" (non-deterministic like UUID()) stmts + TX_RESULT_SET = 128, ///< result set was sent + TX_WITH_SNAPSHOT= 256, ///< WITH CONSISTENT SNAPSHOT was used + TX_LOCKED_TABLES= 512 ///< LOCK TABLES is active +}; + + +/** + Transaction access mode +*/ +enum enum_tx_read_flags { + TX_READ_INHERIT = 0, ///< not explicitly set, inherit session.tx_read_only + TX_READ_ONLY = 1, ///< START TRANSACTION READ ONLY, or tx_read_only=1 + TX_READ_WRITE = 2, ///< START TRANSACTION READ WRITE, or tx_read_only=0 +}; + + +/** + Transaction isolation level +*/ +enum enum_tx_isol_level { + TX_ISOL_INHERIT = 0, ///< not explicitly set, inherit session.tx_isolation + TX_ISOL_UNCOMMITTED = 1, + TX_ISOL_COMMITTED = 2, + TX_ISOL_REPEATABLE = 3, + TX_ISOL_SERIALIZABLE= 4 +}; + + +/** + Transaction tracking level +*/ +enum enum_session_track_transaction_info { + TX_TRACK_NONE = 0, ///< do not send tracker items on transaction info + TX_TRACK_STATE = 1, ///< track transaction status + TX_TRACK_CHISTICS = 2 ///< track status and characteristics +}; + + +/** + This is a tracker class that enables & manages the tracking of + current transaction info for a particular connection. +*/ + +class Transaction_state_tracker : public State_tracker +{ +private: + /** Helper function: turn table info into table access flag */ + enum_tx_state calc_trx_state(THD *thd, thr_lock_type l, bool has_trx); +public: + /** Constructor */ + Transaction_state_tracker(); + bool enable(THD *thd) + { return update(thd, NULL); } + bool update(THD *thd, set_var *var); + bool store(THD *thd, String *buf); + + /** Change transaction characteristics */ + void set_read_flags(THD *thd, enum enum_tx_read_flags flags); + void set_isol_level(THD *thd, enum enum_tx_isol_level level); + + /** Change transaction state */ + void clear_trx_state(THD *thd, uint clear); + void add_trx_state(THD *thd, uint add); + void inline add_trx_state(THD *thd, thr_lock_type l, bool has_trx) + { + add_trx_state(thd, calc_trx_state(thd, l, has_trx)); + } + void add_trx_state_from_thd(THD *thd); + void end_trx(THD *thd); + + +private: + enum enum_tx_changed { + TX_CHG_NONE = 0, ///< no changes from previous stmt + TX_CHG_STATE = 1, ///< state has changed from previous stmt + TX_CHG_CHISTICS = 2 ///< characteristics have changed from previous stmt + }; + + /** any trackable changes caused by this statement? */ + uint tx_changed; + + /** transaction state */ + uint tx_curr_state, tx_reported_state; + + /** r/w or r/o set? session default? */ + enum enum_tx_read_flags tx_read_flags; + + /** isolation level */ + enum enum_tx_isol_level tx_isol_level; + + void reset(); + + inline void update_change_flags(THD *thd) + { + tx_changed &= ~TX_CHG_STATE; + tx_changed |= (tx_curr_state != tx_reported_state) ? TX_CHG_STATE : 0; + if (tx_changed != TX_CHG_NONE) + mark_as_changed(thd, NULL); + } +}; + +#define TRANSACT_TRACKER(X) \ + do { if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) \ + {((Transaction_state_tracker *) \ + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER)) \ + ->X; } } while(0) +#define SESSION_TRACKER_CHANGED(A,B,C) \ + thd->session_tracker.mark_as_changed(A,B,C) +#else + +#define TRANSACT_TRACKER(X) do{}while(0) +#define SESSION_TRACKER_CHANGED(A,B,C) do{}while(0) + +#endif //EMBEDDED_LIBRARY + #endif /* SESSION_TRACKER_INCLUDED */ diff --git a/sql/set_var.cc b/sql/set_var.cc index 84ed7810650..26eb5127a0b 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -220,14 +220,13 @@ bool sys_var::update(THD *thd, set_var *var) */ if ((var->type == OPT_SESSION) && (!ret)) { - thd->session_tracker.mark_as_changed(thd, SESSION_SYSVARS_TRACKER, - (LEX_CSTRING*)var->var); + SESSION_TRACKER_CHANGED(thd, SESSION_SYSVARS_TRACKER, + (LEX_CSTRING*)var->var); /* Here MySQL sends variable name to avoid reporting change of the tracker itself, but we decided that it is not needed */ - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, - NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); } return ret; @@ -894,7 +893,7 @@ int set_var_user::update(THD *thd) return -1; } - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); return 0; } @@ -1002,6 +1001,7 @@ int set_var_collation_client::update(THD *thd) character_set_results); /* Mark client collation variables as changed */ +#ifndef EMBEDDED_LIBRARY if (thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->is_enabled()) { sys_var *svar; @@ -1024,6 +1024,7 @@ int set_var_collation_client::update(THD *thd) mysql_mutex_unlock(&LOCK_plugin); } thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL); +#endif //EMBEDDED_LIBRARY thd->protocol_text.init(thd); thd->protocol_binary.init(thd); diff --git a/sql/set_var.h b/sql/set_var.h index 16111ad7111..ba8027edc72 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -49,8 +49,6 @@ int mysql_add_sys_var_chain(sys_var *chain); int mysql_del_sys_var_chain(sys_var *chain); -extern const LEX_CSTRING SESSION_TRACK_SYSTEM_VARIABLES_NAME; - /** A class representing one system variable - that is something that can be accessed as @@global.variable_name or @@session.variable_name, diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 4e301a9df02..361d68ff76a 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7150,10 +7150,6 @@ skip-to-error-number 3000 ER_MYSQL_57_TEST eng "5.7 test" -ER_NET_OK_PACKET_TOO_LARGE 08S01 - eng "OK packet too large" - ukr "Пакет OK надто великий" - # MariaDB extra error numbers starts from 4000 skip-to-error-number 4000 diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 8488e8dfd62..c344a6c6ed8 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2048,6 +2048,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) break; } } + + TRANSACT_TRACKER(add_trx_state_from_thd(thd)); } /* @@ -3063,6 +3065,9 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, what is needed from the substatement gained */ thd->transaction.stmt.modified_non_trans_table |= parent_modified_non_trans_table; + + TRANSACT_TRACKER(add_trx_state_from_thd(thd)); + /* Unlike for PS we should not call Item's destructors for newly created items after execution of each instruction in stored routine. This is diff --git a/sql/sql_base.cc b/sql/sql_base.cc index d7812db53bd..7757068b265 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2093,6 +2093,9 @@ Locked_tables_list::init_locked_tables(THD *thd) return TRUE; } } + + TRANSACT_TRACKER(add_trx_state(thd, TX_LOCKED_TABLES)); + thd->enter_locked_tables_mode(LTM_LOCK_TABLES); return FALSE; @@ -2133,6 +2136,8 @@ Locked_tables_list::unlock_locked_tables(THD *thd) } thd->leave_locked_tables_mode(); + TRANSACT_TRACKER(clear_trx_state(thd, TX_LOCKED_TABLES)); + DBUG_ASSERT(thd->transaction.stmt.is_empty()); close_thread_tables(thd); @@ -4354,6 +4359,13 @@ static bool check_lock_and_start_stmt(THD *thd, table_list->table->file->print_error(error, MYF(0)); DBUG_RETURN(1); } + + /* + Record in transaction state tracking + */ + TRANSACT_TRACKER(add_trx_state(thd, lock_type, + table_list->table->file->has_transactions())); + DBUG_RETURN(0); } diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 06c5f992939..8ff4684f0ff 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1381,6 +1381,21 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) DBUG_VOID_RETURN; } + /* + Do not store queries while tracking transaction state. + The tracker already flags queries that actually have + transaction tracker items, but this will make behavior + more straight forward. + */ +#ifndef EMBEDDED_LIBRARY + if (thd->variables.session_track_transaction_info != TX_TRACK_NONE) + { + DBUG_PRINT("qcache", ("Do not work with transaction tracking")); + DBUG_VOID_RETURN; + } +#endif //EMBEDDED_LIBRARY + + /* The following assert fails if we haven't called send_result_to_client */ DBUG_ASSERT(thd->base_query.is_alloced() || thd->base_query.ptr() == thd->query()); @@ -1719,6 +1734,20 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) goto err; } + /* + Don't allow serving from Query_cache while tracking transaction + state. This is a safeguard in case an otherwise matching query + was added to the cache before tracking was turned on. + */ +#ifndef EMBEDDED_LIBRARY + if (thd->variables.session_track_transaction_info != TX_TRACK_NONE) + { + DBUG_PRINT("qcache", ("Do not work with transaction tracking")); + goto err; + } +#endif //EMBEDDED_LIBRARY + + thd->query_cache_is_applicable= 1; sql= org_sql; sql_end= sql + query_length; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index a99e375cfbd..54bc4b9959f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1466,7 +1466,9 @@ void THD::init(void) debug_sync_init_thread(this); #endif /* defined(ENABLED_DEBUG_SYNC) */ +#ifndef EMBEDDED_LIBRARY session_tracker.enable(this); +#endif //EMBEDDED_LIBRARY apc_target.init(&LOCK_thd_data); DBUG_VOID_RETURN; @@ -1768,7 +1770,9 @@ THD::~THD() status_var.local_memory_used-= sizeof(THD); /* trick to make happy memory accounting system */ +#ifndef EMBEDDED_LIBRARY session_tracker.deinit(); +#endif //EMBEDDED_LIBRARY if (status_var.local_memory_used != 0) { diff --git a/sql/sql_class.h b/sql/sql_class.h index a6af33f7c5a..1af6d1d87d3 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -689,10 +689,11 @@ typedef struct system_variables my_bool pseudo_slave_mode; + char *session_track_system_variables; + ulong session_track_transaction_info; my_bool session_track_schema; my_bool session_track_state_change; - char *session_track_system_variables; } SV; /** @@ -4059,7 +4060,9 @@ private: LEX_STRING invoker_host; public: +#ifndef EMBEDDED_LIBRARY Session_tracker session_tracker; +#endif //EMBEDDED_LIBRARY /* Flag, mutex and condition for a thread to wait for a signal from another thread. @@ -4293,6 +4296,8 @@ my_eof(THD *thd) { thd->set_row_count_func(-1); thd->get_stmt_da()->set_eof_status(thd); + + TRANSACT_TRACKER(add_trx_state(thd, TX_RESULT_SET)); } #define tmp_disable_binlog(A) \ diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 128281c7686..20538fe1fb4 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1037,7 +1037,7 @@ exit: if (thd->db && cmp_db_names(thd->db, db) && !error) { mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); - thd->session_tracker.mark_as_changed(thd, CURRENT_SCHEMA_TRACKER, NULL); + SESSION_TRACKER_CHANGED(thd, CURRENT_SCHEMA_TRACKER, NULL); } my_dirend(dirp); DBUG_RETURN(error); @@ -1591,8 +1591,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) mysql_change_db_impl(thd, &new_db_file_name, db_access, db_default_cl); done: - thd->session_tracker.mark_as_changed(thd, CURRENT_SCHEMA_TRACKER, NULL); - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL); + SESSION_TRACKER_CHANGED(thd, CURRENT_SCHEMA_TRACKER, NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); DBUG_RETURN(FALSE); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7cb97d156cb..8dc34c2dfe2 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3855,9 +3855,9 @@ mysql_execute_command(THD *thd) /* in case of create temp tables if @@session_track_state_change is ON then send session state notification in OK packet */ if(create_info.options & HA_LEX_CREATE_TMP_TABLE) - thd->session_tracker.mark_as_changed(thd, - SESSION_STATE_CHANGE_TRACKER, - NULL); + { + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); + } my_ok(thd); } } @@ -4619,8 +4619,7 @@ end_with_restore_list: send the boolean tracker in the OK packet */ if(!res && (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) { - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, - NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); } break; } @@ -5433,8 +5432,7 @@ end_with_restore_list: else { /* Reset the isolation level and access mode if no chaining transaction.*/ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); } /* Disconnect the current client connection. */ if (tx_release) @@ -5481,8 +5479,7 @@ end_with_restore_list: else { /* Reset the isolation level and access mode if no chaining transaction.*/ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); } /* Disconnect the current client connection. */ if (tx_release) @@ -5967,8 +5964,7 @@ end_with_restore_list: We've just done a commit, reset transaction isolation level and access mode to the session default. */ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); my_ok(thd); break; } @@ -5986,8 +5982,7 @@ end_with_restore_list: We've just done a rollback, reset transaction isolation level and access mode to the session default. */ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); my_ok(thd); break; } @@ -6205,6 +6200,9 @@ finish: { thd->mdl_context.release_statement_locks(); } + + TRANSACT_TRACKER(add_trx_state_from_thd(thd)); + WSREP_TO_ISOLATION_END; #ifdef WITH_WSREP diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index db6a4b9b15b..b715e33ae62 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -323,8 +323,6 @@ static void unlock_variables(THD *thd, struct system_variables *vars); static void cleanup_variables(struct system_variables *vars); static void plugin_vars_free_values(sys_var *vars); static void restore_ptr_backup(uint n, st_ptr_backup *backup); -#define my_intern_plugin_lock(A,B) intern_plugin_lock(A,B) -#define my_intern_plugin_lock_ci(A,B) intern_plugin_lock(A,B) static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin); static void intern_plugin_unlock(LEX *lex, plugin_ref plugin); static void reap_plugins(void); @@ -2806,7 +2804,7 @@ sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length, { mysql_rwlock_unlock(&LOCK_system_variables_hash); LEX *lex= thd ? thd->lex : 0; - if (!(plugin= my_intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin)))) + if (!(plugin= intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin)))) var= NULL; /* failed to lock it, it must be uninstalling */ else if (!(plugin_state(plugin) & PLUGIN_IS_READY)) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index cc41bd6284e..eab2863588d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2759,8 +2759,7 @@ void mysql_sql_stmt_prepare(THD *thd) } else { - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, - NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); my_ok(thd, 0L, 0L, "Statement prepared"); } @@ -3212,8 +3211,7 @@ void mysql_sql_stmt_close(THD *thd) else { stmt->deallocate(); - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, - NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); my_ok(thd); } } diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 28e7b899133..a5f266b2d2c 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -1161,13 +1161,15 @@ uint convert_to_printable(char *to, size_t to_len, void String::q_net_store_length(ulonglong length) { + DBUG_ASSERT(Alloced_length >= (str_length + net_length_size(length))); char *pos= (char *) net_store_length((uchar *)(Ptr + str_length), length); str_length= pos - Ptr; } void String::q_net_store_data(const uchar *from, size_t length) { + DBUG_ASSERT(Alloced_length >= (str_length + length + + net_length_size(length))); q_net_store_length(length); - bool res= append((const char *)from, length); - DBUG_ASSERT(!res); + q_append((const char *)from, length); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index bed116a2930..7784a2b188a 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -9230,8 +9230,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, goto err_new_table_cleanup; } /* in case of alter temp table send the tracker in OK packet */ - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, - NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 68be0cdbdca..462bfe52741 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3363,6 +3363,20 @@ bool Sys_var_tx_read_only::session_update(THD *thd, set_var *var) { // @see Sys_var_tx_isolation::session_update() above for the rules. thd->tx_read_only= var->save_result.ulonglong_value; + +#ifndef EMBEDDED_LIBRARY + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + { + Transaction_state_tracker *tst= (Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER); + + if (var->type == OPT_DEFAULT) + tst->set_read_flags(thd, + thd->tx_read_only ? TX_READ_ONLY : TX_READ_WRITE); + else + tst->set_read_flags(thd, TX_READ_INHERIT); + } +#endif //EMBEDDED_LIBRARY } return false; } @@ -5375,11 +5389,10 @@ static Sys_var_ulong Sys_log_tc_size( BLOCK_SIZE(my_getpagesize())); #endif -const LEX_CSTRING SESSION_TRACK_SYSTEM_VARIABLES_NAME= - {STRING_WITH_LEN("session_track_system_variables")}; +#ifndef EMBEDDED_LIBRARY static Sys_var_sesvartrack Sys_track_session_sys_vars( - SESSION_TRACK_SYSTEM_VARIABLES_NAME.str, + "session_track_system_variables", "Track changes in registered system variables.", CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET, DEFAULT("autocommit,character_set_client,character_set_connection," @@ -5390,30 +5403,61 @@ static bool update_session_track_schema(sys_var *self, THD *thd, enum_var_type type) { DBUG_ENTER("update_session_track_schema"); - DBUG_RETURN(thd->session_tracker.get_tracker(CURRENT_SCHEMA_TRACKER)->update(thd)); + DBUG_RETURN(thd->session_tracker.get_tracker(CURRENT_SCHEMA_TRACKER)-> + update(thd, NULL)); } static Sys_var_mybool Sys_session_track_schema( "session_track_schema", - "Track changes to the 'default schema'.", + "Track changes to the default schema.", SESSION_VAR(session_track_schema), CMD_LINE(OPT_ARG), DEFAULT(TRUE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(update_session_track_schema)); + +static bool update_session_track_tx_info(sys_var *self, THD *thd, + enum_var_type type) +{ + DBUG_ENTER("update_session_track_tx_info"); + DBUG_RETURN(thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER)-> + update(thd, NULL)); +} + +static const char *session_track_transaction_info_names[]= + { "OFF", "STATE", "CHARACTERISTICS", NullS }; + +static Sys_var_enum Sys_session_track_transaction_info( + "session_track_transaction_info", + "Track changes to the transaction attributes. OFF to disable; " + "STATE to track just transaction state (Is there an active transaction? " + "Does it have any data? etc.); CHARACTERISTICS to track transaction " + "state and report all statements needed to start a transaction with" + "the same characteristics (isolation level, read only/read write," + "snapshot - but not any work done / data modified within the " + "transaction).", + SESSION_VAR(session_track_transaction_info), + CMD_LINE(REQUIRED_ARG), session_track_transaction_info_names, + DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(update_session_track_tx_info)); + + static bool update_session_track_state_change(sys_var *self, THD *thd, enum_var_type type) { DBUG_ENTER("update_session_track_state_change"); - DBUG_RETURN(thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->update(thd)); + DBUG_RETURN(thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)-> + update(thd, NULL)); } static Sys_var_mybool Sys_session_track_state_change( "session_track_state_change", - "Track changes to the 'session state'.", + "Track changes to the session state.", SESSION_VAR(session_track_state_change), CMD_LINE(OPT_ARG), DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(update_session_track_state_change)); + +#endif //EMBEDDED_LIBRARY diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index dbe84d3efcc..6f17e768d95 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -567,6 +567,7 @@ public: { DBUG_ASSERT(FALSE); } }; +#ifndef EMBEDDED_LIBRARY class Sys_var_sesvartrack: public Sys_var_charptr_base { public: @@ -602,7 +603,7 @@ public: } bool session_update(THD *thd, set_var *var) { - return sysvartrack_update(thd); + return sysvartrack_update(thd, var); } void session_save_default(THD *thd, set_var *var) { @@ -623,19 +624,17 @@ public: { DBUG_ASSERT(thd != NULL); size_t len= sysvartrack_value_len(thd); - char *res= 0; - char *buf= (char *)my_safe_alloca(len); - if (buf && !sysvartrack_value_construct(thd, buf, len)) + char *res= (char *)thd->alloc(len + sizeof(char *)); + if (res) { - size_t len= strlen(buf) + 1; - res= (char*) thd->alloc(len + sizeof(char *)); - if (res) - memcpy((*((char**) res)= res + sizeof(char *)), buf, len); - my_safe_afree(buf, len); + char *buf= res + sizeof(char *); + *((char**) res)= buf; + sysvartrack_value_construct(thd, buf, len); } return (uchar *)res; } }; +#endif //EMBEDDED_LIBRARY class Sys_var_proxy_user: public sys_var @@ -2079,7 +2078,47 @@ public: if (var->type == OPT_SESSION && Sys_var_enum::session_update(thd, var)) return TRUE; if (var->type == OPT_DEFAULT || !thd->in_active_multi_stmt_transaction()) + { +#ifndef EMBEDDED_LIBRARY + Transaction_state_tracker *tst= NULL; + + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + tst= (Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER); +#endif //EMBEDDED_LIBRARY + thd->tx_isolation= (enum_tx_isolation) var->save_result.ulonglong_value; + +#ifndef EMBEDDED_LIBRARY + if (var->type == OPT_DEFAULT) + { + enum enum_tx_isol_level l; + switch (thd->tx_isolation) { + case ISO_READ_UNCOMMITTED: + l= TX_ISOL_UNCOMMITTED; + break; + case ISO_READ_COMMITTED: + l= TX_ISOL_COMMITTED; + break; + case ISO_REPEATABLE_READ: + l= TX_ISOL_REPEATABLE; + break; + case ISO_SERIALIZABLE: + l= TX_ISOL_SERIALIZABLE; + break; + default: + DBUG_ASSERT(0); + return TRUE; + } + if (tst) + tst->set_isol_level(thd, l); + } + else if (tst) + { + tst->set_isol_level(thd, TX_ISOL_INHERIT); + } +#endif //EMBEDDED_LIBRARY + } return FALSE; } }; diff --git a/sql/transaction.cc b/sql/transaction.cc index 8b188709ce6..d728ea25b65 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -25,6 +25,43 @@ #include "debug_sync.h" // DEBUG_SYNC #include "sql_acl.h" + +#ifndef EMBEDDED_LIBRARY +/** + Helper: Tell tracker (if any) that transaction ended. +*/ +static void trans_track_end_trx(THD *thd) +{ + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + { + ((Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER))->end_trx(thd); + } +} + + +/** + Helper: transaction ended, SET TRANSACTION one-shot variables + revert to session values. Let the transaction state tracker know. +*/ +void trans_reset_one_shot_chistics(THD *thd) +{ + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + { + Transaction_state_tracker *tst= (Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER); + + tst->set_read_flags(thd, TX_READ_INHERIT); + tst->set_isol_level(thd, TX_ISOL_INHERIT); + } + + thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; + thd->tx_read_only= thd->variables.tx_read_only; +} +#else +#define trans_track_end_trx(A) do{}while(0) +#endif //EMBEDDED_LIBRARY + /* Conditions under which the transaction state must not change. */ static bool trans_check(THD *thd) { @@ -125,11 +162,20 @@ static bool xa_trans_force_rollback(THD *thd) bool trans_begin(THD *thd, uint flags) { int res= FALSE; +#ifndef EMBEDDED_LIBRARY + Transaction_state_tracker *tst= NULL; +#endif //EMBEDDED_LIBRARY DBUG_ENTER("trans_begin"); if (trans_check(thd)) DBUG_RETURN(TRUE); +#ifndef EMBEDDED_LIBRARY + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + tst= (Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER); +#endif //EMBEDDED_LIBRARY + thd->locked_tables_list.unlock_locked_tables(thd); DBUG_ASSERT(!thd->locked_tables_mode); @@ -172,7 +218,13 @@ bool trans_begin(THD *thd, uint flags) DBUG_ASSERT(!((flags & MYSQL_START_TRANS_OPT_READ_ONLY) && (flags & MYSQL_START_TRANS_OPT_READ_WRITE))); if (flags & MYSQL_START_TRANS_OPT_READ_ONLY) + { thd->tx_read_only= true; +#ifndef EMBEDDED_LIBRARY + if (tst) + tst->set_read_flags(thd, TX_READ_ONLY); +#endif //EMBEDDED_LIBRARY + } else if (flags & MYSQL_START_TRANS_OPT_READ_WRITE) { /* @@ -189,6 +241,14 @@ bool trans_begin(THD *thd, uint flags) DBUG_RETURN(true); } thd->tx_read_only= false; + /* + This flags that tx_read_only was set explicitly, rather than + just from the session's default. + */ +#ifndef EMBEDDED_LIBRARY + if (tst) + tst->set_read_flags(thd, TX_READ_WRITE); +#endif //EMBEDDED_LIBRARY } #ifdef WITH_WSREP @@ -203,9 +263,20 @@ bool trans_begin(THD *thd, uint flags) thd->server_status|= SERVER_STATUS_IN_TRANS_READONLY; DBUG_PRINT("info", ("setting SERVER_STATUS_IN_TRANS")); +#ifndef EMBEDDED_LIBRARY + if (tst) + tst->add_trx_state(thd, TX_EXPLICIT); +#endif //EMBEDDED_LIBRARY + /* ha_start_consistent_snapshot() relies on OPTION_BEGIN flag set. */ if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) + { +#ifndef EMBEDDED_LIBRARY + if (tst) + tst->add_trx_state(thd, TX_WITH_SNAPSHOT); +#endif //EMBEDDED_LIBRARY res= ha_start_consistent_snapshot(thd); + } DBUG_RETURN(MY_TEST(res)); } @@ -255,6 +326,8 @@ bool trans_commit(THD *thd) thd->transaction.all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT; thd->lex->start_transaction_opt= 0; + trans_track_end_trx(thd); + DBUG_RETURN(MY_TEST(res)); } @@ -308,8 +381,9 @@ bool trans_commit_implicit(THD *thd) @@session.completion_type since it's documented to not have any effect on implicit commit. */ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); + + trans_track_end_trx(thd); DBUG_RETURN(res); } @@ -349,6 +423,8 @@ bool trans_rollback(THD *thd) thd->transaction.all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT; thd->lex->start_transaction_opt= 0; + trans_track_end_trx(thd); + DBUG_RETURN(MY_TEST(res)); } @@ -396,6 +472,8 @@ bool trans_rollback_implicit(THD *thd) /* Rollback should clear transaction_rollback_request flag. */ DBUG_ASSERT(! thd->transaction_rollback_request); + trans_track_end_trx(thd); + DBUG_RETURN(MY_TEST(res)); } @@ -434,8 +512,7 @@ bool trans_commit_stmt(THD *thd) res= ha_commit_trans(thd, FALSE); if (! thd->in_active_multi_stmt_transaction()) { - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); if (WSREP_ON) wsrep_post_commit(thd, FALSE); } @@ -487,10 +564,7 @@ bool trans_rollback_stmt(THD *thd) wsrep_register_hton(thd, FALSE); ha_rollback_trans(thd, FALSE); if (! thd->in_active_multi_stmt_transaction()) - { - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; - } + trans_reset_one_shot_chistics(thd); } (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); @@ -912,6 +986,8 @@ bool trans_xa_commit(THD *thd) xid_cache_delete(thd, &thd->transaction.xid_state); thd->transaction.xid_state.xa_state= XA_NOTR; + trans_track_end_trx(thd); + DBUG_RETURN(res); } @@ -968,5 +1044,7 @@ bool trans_xa_rollback(THD *thd) xid_cache_delete(thd, &thd->transaction.xid_state); thd->transaction.xid_state.xa_state= XA_NOTR; + trans_track_end_trx(thd); + DBUG_RETURN(res); } diff --git a/sql/transaction.h b/sql/transaction.h index 54b25f1de2a..040f1a453cd 100644 --- a/sql/transaction.h +++ b/sql/transaction.h @@ -44,4 +44,10 @@ bool trans_xa_prepare(THD *thd); bool trans_xa_commit(THD *thd); bool trans_xa_rollback(THD *thd); +#ifndef EMBEDDED_LIBRARY +void trans_reset_one_shot_chistics(THD *thd); +#else +#define trans_reset_one_shot_chistics(A) do{}while(0) +#endif //EMBEDDED_LIBRARY + #endif /* TRANSACTION_H */ |