diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/CMakeLists.txt | 4 | ||||
-rw-r--r-- | sql/net_serv.cc | 1 | ||||
-rw-r--r-- | sql/protocol.cc | 138 | ||||
-rw-r--r-- | sql/session_tracker.cc | 358 | ||||
-rw-r--r-- | sql/session_tracker.h | 159 | ||||
-rw-r--r-- | sql/set_var.cc | 32 | ||||
-rw-r--r-- | sql/set_var.h | 2 | ||||
-rw-r--r-- | sql/share/errmsg-utf8.txt | 5 | ||||
-rw-r--r-- | sql/sp_head.cc | 10 | ||||
-rw-r--r-- | sql/sql_class.cc | 3 | ||||
-rw-r--r-- | sql/sql_class.h | 4 | ||||
-rw-r--r-- | sql/sql_db.cc | 14 | ||||
-rw-r--r-- | sql/sql_parse.cc | 14 | ||||
-rw-r--r-- | sql/sql_plugin.cc | 2 | ||||
-rw-r--r-- | sql/sql_plugin.h | 2 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 6 | ||||
-rw-r--r-- | sql/sql_string.cc | 14 | ||||
-rw-r--r-- | sql/sql_string.h | 18 | ||||
-rw-r--r-- | sql/sql_table.cc | 4 | ||||
-rw-r--r-- | sql/sys_vars.cc | 33 |
20 files changed, 780 insertions, 43 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 089d793b2b0..a18294e5ae3 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -95,7 +95,9 @@ SET (SQL_SOURCE ../sql-common/client_plugin.c opt_range.cc opt_range.h opt_sum.cc ../sql-common/pack.c parse_file.cc password.c procedure.cc - protocol.cc records.cc repl_failsafe.cc rpl_filter.cc set_var.cc + protocol.cc records.cc repl_failsafe.cc rpl_filter.cc + session_tracker.cc + set_var.cc slave.cc sp.cc sp_cache.cc sp_head.cc sp_pcontext.cc sp_rcontext.cc spatial.cc sql_acl.cc sql_analyse.cc sql_base.cc sql_cache.cc sql_class.cc sql_client.cc sql_crypt.cc sql_crypt.h diff --git a/sql/net_serv.cc b/sql/net_serv.cc index f0284462206..dc97d5e8e54 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -117,7 +117,6 @@ extern my_bool thd_net_is_killed(); #endif #define TEST_BLOCKING 8 -#define MAX_PACKET_LENGTH (256L*256L*256L-1) static my_bool net_write_buff(NET *, const uchar *, ulong); diff --git a/sql/protocol.cc b/sql/protocol.cc index 608ec553da0..e12c72dd988 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -35,7 +35,8 @@ static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024; /* Declared non-static only because of the embedded library. */ bool net_send_error_packet(THD *, uint, const char *, const char *); /* Declared non-static only because of the embedded library. */ -bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *, bool); +bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *, + bool, bool); /* Declared non-static only because of the embedded library. */ bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count); #ifndef EMBEDDED_LIBRARY @@ -197,7 +198,8 @@ 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 + @return @retval FALSE The message was successfully sent @retval TRUE An error occurred and the messages wasn't sent properly @@ -209,10 +211,18 @@ bool net_send_ok(THD *thd, uint server_status, uint statement_warn_count, ulonglong affected_rows, ulonglong id, const char *message, + bool is_eof, bool skip_flush) { NET *net= &thd->net; - uchar buff[MYSQL_ERRMSG_SIZE+10],*pos; + 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; DBUG_ENTER("net_send_ok"); @@ -222,38 +232,82 @@ net_send_ok(THD *thd, DBUG_RETURN(FALSE); } - buff[0]=0; // No fields - pos=net_store_length(buff+1,affected_rows); - pos=net_store_length(pos, id); + /* + OK send instead of EOF still require 0xFE header, but OK packet content. + */ + if (is_eof) + { + DBUG_ASSERT(thd->client_capabilities & CLIENT_DEPRECATE_EOF); + store.q_append((char)254); + } + else + store.q_append('\0'); + + /* affected rows */ + store.q_net_store_length(affected_rows); + + /* last insert id */ + store.q_net_store_length(id); + if (thd->client_capabilities & CLIENT_PROTOCOL_41) { DBUG_PRINT("info", ("affected_rows: %lu id: %lu status: %u warning_count: %u", - (ulong) affected_rows, + (ulong) affected_rows, (ulong) id, (uint) (server_status & 0xffff), (uint) statement_warn_count)); - int2store(pos, server_status); - pos+=2; + store.q_append2b(server_status); /* We can only return up to 65535 warnings in two bytes */ uint tmp= MY_MIN(statement_warn_count, 65535); - int2store(pos, tmp); - pos+= 2; + store.q_append2b(tmp); } else if (net->return_status) // For 4.0 protocol { - int2store(pos, server_status); - pos+=2; + store.q_append2b(server_status); } thd->get_stmt_da()->set_overwrite_status(true); - if (message && message[0]) - pos= net_store_data(pos, (uchar*) message, strlen(message)); - error= my_net_write(net, buff, (size_t) (pos-buff)); - if (!error && !skip_flush) + 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])) + { + DBUG_ASSERT(strlen(message) <= MYSQL_ERRMSG_SIZE); + 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); + + thd->session_tracker.store(thd, &store); + } + } + else if (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)); + } + + if (store.length() > MAX_PACKET_LENGTH) + { + 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); + } + error= my_net_write(net, (const unsigned char*)store.ptr(), store.length()); + if (!error && (!skip_flush || is_eof)) error= net_flush(net); + thd->server_status&= ~SERVER_SESSION_STATE_CHANGED; thd->get_stmt_da()->set_overwrite_status(false); DBUG_PRINT("info", ("OK sent, so no more error sending allowed")); @@ -261,6 +315,7 @@ net_send_ok(THD *thd, DBUG_RETURN(error); } + static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */ /** @@ -292,6 +347,22 @@ net_send_eof(THD *thd, uint server_status, uint statement_warn_count) NET *net= &thd->net; bool error= FALSE; DBUG_ENTER("net_send_eof"); + + /* + Check if client understand new format packets (OK instead of EOF) + + Normally end of statement reply is signaled by OK packet, but in case + of binlog dump request an EOF packet is sent instead. Also, old clients + expect EOF packet instead of OK + */ + if ((thd->client_capabilities & CLIENT_DEPRECATE_EOF) && + (thd->get_command() != COM_BINLOG_DUMP )) + { + error= net_send_ok(thd, server_status, statement_warn_count, 0, 0, NULL, + true, false); + DBUG_RETURN(error); + } + /* Set to TRUE if no active vio, to work well in case of --init-file */ if (net->vio != 0) { @@ -546,9 +617,9 @@ bool Protocol::send_ok(uint server_status, uint statement_warn_count, const char *message, bool skip_flush) { DBUG_ENTER("Protocol::send_ok"); - const bool retval= + const bool retval= net_send_ok(thd, server_status, statement_warn_count, - affected_rows, last_insert_id, message, skip_flush); + affected_rows, last_insert_id, message, false, skip_flush); DBUG_RETURN(retval); } @@ -562,7 +633,7 @@ bool Protocol::send_ok(uint server_status, uint statement_warn_count, bool Protocol::send_eof(uint server_status, uint statement_warn_count) { DBUG_ENTER("Protocol::send_eof"); - const bool retval= net_send_eof(thd, server_status, statement_warn_count); + bool retval= net_send_eof(thd, server_status, statement_warn_count); DBUG_RETURN(retval); } @@ -862,14 +933,19 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) if (flags & SEND_EOF) { - /* - Mark the end of meta-data result set, and store thd->server_status, - to show that there is no cursor. - Send no warning information, as it will be sent at statement end. - */ - if (write_eof_packet(thd, &thd->net, thd->server_status, - thd->get_stmt_da()->current_statement_warn_count())) - DBUG_RETURN(1); + + /* if it is new client do not send EOF packet */ + if (!(thd->client_capabilities & CLIENT_DEPRECATE_EOF)) + { + /* + Mark the end of meta-data result set, and store thd->server_status, + to show that there is no cursor. + Send no warning information, as it will be sent at statement end. + */ + if (write_eof_packet(thd, &thd->net, thd->server_status, + thd->get_stmt_da()->current_statement_warn_count())) + DBUG_RETURN(1); + } } DBUG_RETURN(prepare_for_send(list->elements)); @@ -1505,6 +1581,7 @@ bool Protocol_binary::store_time(MYSQL_TIME *tm, int decimals) bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params) { + bool ret; if (!(thd->client_capabilities & CLIENT_PS_MULTI_RESULTS)) { /* The client does not support OUT-parameters. */ @@ -1558,8 +1635,7 @@ bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params) /* Restore THD::server_status. */ thd->server_status&= ~SERVER_PS_OUT_PARAMS; - /* Send EOF-packet. */ - net_send_eof(thd, thd->server_status, 0); + ret= net_send_eof(thd, thd->server_status, 0); /* Reset SERVER_MORE_RESULTS_EXISTS bit, because this is the last packet @@ -1567,5 +1643,5 @@ bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params) */ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; - return FALSE; + return ret ? FALSE : TRUE; } diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc new file mode 100644 index 00000000000..ad9906d7159 --- /dev/null +++ b/sql/session_tracker.cc @@ -0,0 +1,358 @@ +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2016, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#include "session_tracker.h" + +#include "hash.h" +#include "table.h" +#include "rpl_gtid.h" +#include "sql_class.h" +#include "sql_show.h" +#include "sql_plugin.h" + +class Not_implemented_tracker : public State_tracker +{ +public: + bool enable(THD *thd) + { return false; } + bool check(THD *, set_var *) + { return false; } + bool update(THD *) + { return false; } + bool store(THD *, String *) + { return false; } + void mark_as_changed(THD *, LEX_CSTRING *tracked_item_name) + {} + +}; + + +/** + Current_schema_tracker, + + This is a tracker class that enables & manages the tracking of current + schema for a particular connection. +*/ + +class Current_schema_tracker : public State_tracker +{ +private: + bool schema_track_inited; + void reset(); + +public: + + Current_schema_tracker() + { + schema_track_inited= false; + } + + bool enable(THD *thd) + { return update(thd); } + bool check(THD *thd, set_var *var) + { return false; } + bool update(THD *thd); + bool store(THD *thd, String *buf); + void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name); +}; + +/* + Session_state_change_tracker + + This is a boolean tracker class that will monitor any change that contributes + to a session state change. + Attributes that contribute to session state change include: + - Successful change to System variables + - User defined variables assignments + - temporary tables created, altered or deleted + - prepared statements added or removed + - change in current database + - change of current role +*/ + +class Session_state_change_tracker : public State_tracker +{ +private: + + void reset(); + +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); + 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) + {} +}; + + +/* To be used in expanding the buffer. */ +static const unsigned int EXTRA_ALLOC= 1024; + +/////////////////////////////////////////////////////////////////////////////// + +/** + Enable/disable the tracker based on @@session_track_schema's value. + + @param thd [IN] The thd handle. + + @return + false (always) +*/ + +bool Current_schema_tracker::update(THD *thd) +{ + m_enabled= thd->variables.session_track_schema; + return false; +} + + +/** + Store the schema name as length-encoded string in the specified buffer. + + @param thd [IN] The thd handle. + @paran buf [INOUT] Buffer to store the information to. + + @reval false Success + @retval true Error +*/ + +bool Current_schema_tracker::store(THD *thd, String *buf) +{ + ulonglong db_length, length; + + /* + Protocol made (by unknown reasons) redundant: + It saves length of database name and name of database name + + length of saved length of database length. + */ + length= db_length= thd->db_length; + length += net_length_size(length); + + 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)) + return true; + + /* Session state type (SESSION_TRACK_SCHEMA) */ + buf->q_net_store_length((ulonglong)SESSION_TRACK_SCHEMA); + + /* Length of the overall entity. */ + buf->q_net_store_length(length); + + /* Length and current schema name */ + buf->q_net_store_data((const uchar *)thd->db, thd->db_length); + + reset(); + + return false; +} + + +/** + Mark the tracker as changed. +*/ + +void Current_schema_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. + + @return void +*/ + +void Current_schema_tracker::reset() +{ + m_changed= false; +} + + +/////////////////////////////////////////////////////////////////////////////// + +Session_state_change_tracker::Session_state_change_tracker() +{ + m_changed= false; +} + +/** + @Enable/disable the tracker based on @@session_track_state_change value. + + @param thd [IN] The thd handle. + @return false (always) + +**/ + +bool Session_state_change_tracker::update(THD *thd) +{ + m_enabled= thd->variables.session_track_state_change; + return false; +} + +/** + Store the '1' in the specified buffer when state is changed. + + @param thd [IN] The thd handle. + @paran buf [INOUT] Buffer to store the information to. + + @reval false Success + @retval true Error +**/ + +bool Session_state_change_tracker::store(THD *thd, String *buf) +{ + if (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); + + /* Length of the overall entity (1 byte) */ + buf->q_append('\1'); + + DBUG_ASSERT(is_state_changed(thd)); + buf->q_append('1'); + + reset(); + + 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. +*/ + +void Session_state_change_tracker::reset() +{ + m_changed= false; +} + +/** + Find if there is a session state change. +*/ + +bool Session_state_change_tracker::is_state_changed(THD *) +{ + return m_changed; +} + +/////////////////////////////////////////////////////////////////////////////// + +/** + @brief Initialize session tracker objects. +*/ + +Session_tracker::Session_tracker() +{ + m_trackers[SESSION_SYSVARS_TRACKER]= + new (std::nothrow) Not_implemented_tracker; + m_trackers[CURRENT_SCHEMA_TRACKER]= + new (std::nothrow) Current_schema_tracker; + m_trackers[SESSION_STATE_CHANGE_TRACKER]= + new (std::nothrow) Session_state_change_tracker; + m_trackers[SESSION_GTIDS_TRACKER]= + new (std::nothrow) Not_implemented_tracker; + m_trackers[TRANSACTION_INFO_TRACKER]= + new (std::nothrow) Not_implemented_tracker; +} + +/** + @brief Enables the tracker objects. + + @param thd [IN] The thread handle. + + @return void +*/ +void Session_tracker::enable(THD *thd) +{ + for (int i= 0; i <= SESSION_TRACKER_END; i ++) + m_trackers[i]->enable(thd); +} + + +/** + @brief Store all change information in the specified buffer. + + @param thd [IN] The thd handle. + @param buf [OUT] Reference to the string buffer to which the state + change data needs to be written. +*/ + +void Session_tracker::store(THD *thd, String *buf) +{ + /* Temporary buffer to store all the changes. */ + size_t start; + + /* + Probably most track result will fit in 251 byte so lets made it at + least efficient. We allocate 1 byte for length and then will move + string if there is more. + */ + buf->append('\0'); + start= buf->length(); + + /* Get total length. */ + for (int i= 0; i <= SESSION_TRACKER_END; i ++) + { + if (m_trackers[i]->is_changed() && + m_trackers[i]->store(thd, buf)) + { + buf->length(start); // it is safer to have 0-length block in case of error + return; + } + } + + size_t length= buf->length() - start; + uchar *data= (uchar *)(buf->ptr() + start); + uint size; + + if ((size= net_length_size(length)) != 1) + { + if (buf->prep_alloc(size - 1, EXTRA_ALLOC)) + { + buf->length(start); // it is safer to have 0-length block in case of error + return; + } + memmove(data + (size - 1), data, length); + } + + net_store_length(data - 1, length); +} diff --git a/sql/session_tracker.h b/sql/session_tracker.h new file mode 100644 index 00000000000..ec24d5a7a00 --- /dev/null +++ b/sql/session_tracker.h @@ -0,0 +1,159 @@ +#ifndef SESSION_TRACKER_INCLUDED +#define SESSION_TRACKER_INCLUDED + +/* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2016, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "m_string.h" +#include "thr_lock.h" + +/* forward declarations */ +class THD; +class set_var; +class String; + + +enum enum_session_tracker +{ + SESSION_SYSVARS_TRACKER, /* Session system variables */ + CURRENT_SCHEMA_TRACKER, /* Current schema */ + SESSION_STATE_CHANGE_TRACKER, + SESSION_GTIDS_TRACKER, /* Tracks GTIDs */ + TRANSACTION_INFO_TRACKER /* Transaction state */ +}; + +#define SESSION_TRACKER_END TRANSACTION_INFO_TRACKER + + +/** + State_tracker + + An abstract class that defines the interface for any of the server's + 'session state change tracker'. A tracker, however, is a sub- class of + this class which takes care of tracking the change in value of a part- + icular session state type and thus defines various methods listed in this + interface. The change information is later serialized and transmitted to + the client through protocol's OK packet. + + Tracker system variables :- + A tracker is normally mapped to a system variable. So in order to enable, + disable or modify the sub-entities of a tracker, the user needs to modify + 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(). +*/ + +class State_tracker +{ +protected: + /** + Is tracking enabled for a particular session state type ? + + @note: It is cache to avoid virtual functions and checking thd + when we want mark tracker as changed. + */ + bool m_enabled; + + /** Has the session state type changed ? */ + bool m_changed; + +public: + /** Constructor */ + State_tracker() : m_enabled(false), m_changed(false) + {} + + /** Destructor */ + virtual ~State_tracker() + {} + + /** Getters */ + bool is_enabled() const + { return m_enabled; } + + bool is_changed() const + { return m_changed; } + + /** 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; + + /** 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; +}; + + +/** + Session_tracker + + This class holds an object each for all tracker classes and provides + methods necessary for systematic detection and generation of session + state change information. +*/ + +class Session_tracker +{ +private: + State_tracker *m_trackers[SESSION_TRACKER_END + 1]; + + /* The following two functions are private to disable copying. */ + Session_tracker(Session_tracker const &other) + { + DBUG_ASSERT(FALSE); + } + Session_tracker& operator= (Session_tracker const &rhs) + { + DBUG_ASSERT(FALSE); + return *this; + } + +public: + + Session_tracker(); + ~Session_tracker() + { + for (int i= 0; i <= SESSION_TRACKER_END; i ++) + delete m_trackers[i]; + } + void enable(THD *thd); + + /** Returns the pointer to the tracker object for the specified tracker. */ + inline State_tracker *get_tracker(enum_session_tracker tracker) const + { + return m_trackers[tracker]; + } + + inline void mark_as_changed(THD *thd, enum enum_session_tracker tracker, + LEX_CSTRING *data) + { + if (m_trackers[tracker]->is_enabled()) + m_trackers[tracker]->mark_as_changed(thd, data); + } + + + void store(THD *thd, String *main_buf); +}; + +#endif /* SESSION_TRACKER_INCLUDED */ diff --git a/sql/set_var.cc b/sql/set_var.cc index b178681e952..68d57abcdf6 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -204,8 +204,28 @@ bool sys_var::update(THD *thd, set_var *var) (on_update && on_update(this, thd, OPT_GLOBAL)); } else - return session_update(thd, var) || + { + bool ret= session_update(thd, var) || (on_update && on_update(this, thd, OPT_SESSION)); + + /* + Make sure we don't session-track variables that are not actually + part of the session. tx_isolation and and tx_read_only for example + exist as GLOBAL, SESSION, and one-shot ("for next transaction only"). + */ + if ((var->type == OPT_SESSION) && (!ret)) + { + /* + 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); + + } + + return ret; + } } uchar *sys_var::session_value_ptr(THD *thd, const LEX_STRING *base) @@ -867,6 +887,8 @@ int set_var_user::update(THD *thd) MYF(0)); return -1; } + + thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL); return 0; } @@ -914,7 +936,11 @@ int set_var_role::check(THD *thd) int set_var_role::update(THD *thd) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - return acl_setrole(thd, role.str, access); + int res= acl_setrole(thd, role.str, access); + if (!res) + thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, + NULL); + return res; #else return 0; #endif @@ -968,6 +994,8 @@ int set_var_collation_client::update(THD *thd) { thd->update_charset(character_set_client, collation_connection, character_set_results); + + thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL); thd->protocol_text.init(thd); thd->protocol_binary.init(thd); return 0; diff --git a/sql/set_var.h b/sql/set_var.h index 060a4e1a57c..6a650f2ec8a 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -385,7 +385,7 @@ extern SHOW_COMP_OPTION have_openssl; SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type type); int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond); -sys_var *find_sys_var(THD *thd, const char *str, uint length=0); +sys_var *find_sys_var(THD *thd, const char *str, size_t length=0); int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free); #define SYSVAR_AUTOSIZE(VAR,VAL) \ diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 8dfa519ba2d..3bb1b3a6197 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7139,7 +7139,6 @@ ER_KILL_QUERY_DENIED_ERROR ER_NO_EIS_FOR_FIELD eng "Engine-independent statistics are not collected for column '%s'" ukr "Незалежна від типу таблиці статистика не збирається для стовбця '%s'" - # # Internal errors, not used # @@ -7151,6 +7150,10 @@ 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 6b048cec68b..8488e8dfd62 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2977,6 +2977,16 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, reinit_stmt_before_use(thd, m_lex); +#ifndef EMBEDDED_LIBRARY + /* + if there was instruction which changed tracking state before, result + can go with this command OK packet, so better do not cache the result. + */ + if ((thd->client_capabilities & CLIENT_SESSION_TRACK) && + (thd->server_status & SERVER_SESSION_STATE_CHANGED)) + thd->lex->safe_to_cache_query= 0; +#endif + if (open_tables) res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d29dc0eff14..e91c80d3f36 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1465,6 +1465,9 @@ void THD::init(void) /* Initialize the Debug Sync Facility. See debug_sync.cc. */ debug_sync_init_thread(this); #endif /* defined(ENABLED_DEBUG_SYNC) */ + + session_tracker.enable(this); + apc_target.init(&LOCK_thd_data); DBUG_VOID_RETURN; } diff --git a/sql/sql_class.h b/sql/sql_class.h index b8c9614a31f..7a663bf7653 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -45,6 +45,7 @@ #include <mysql/psi/mysql_idle.h> #include <mysql/psi/mysql_table.h> #include <mysql_com_server.h> +#include "session_tracker.h" extern "C" void set_thd_stage_info(void *thd, @@ -688,6 +689,8 @@ typedef struct system_variables my_bool pseudo_slave_mode; + my_bool session_track_schema; + my_bool session_track_state_change; } SV; /** @@ -4054,6 +4057,7 @@ private: LEX_STRING invoker_host; public: + Session_tracker session_tracker; /* Flag, mutex and condition for a thread to wait for a signal from another thread. diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 1a0ee03ec34..128281c7686 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1035,7 +1035,10 @@ exit: it to 0. */ 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); + } my_dirend(dirp); DBUG_RETURN(error); } @@ -1459,7 +1462,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); - DBUG_RETURN(FALSE); + goto done; } else { @@ -1476,8 +1479,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) mysql_change_db_impl(thd, &INFORMATION_SCHEMA_NAME, SELECT_ACL, system_charset_info); - - DBUG_RETURN(FALSE); + goto done; } /* @@ -1564,8 +1566,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); /* The operation succeed. */ - - DBUG_RETURN(FALSE); + goto done; } else { @@ -1589,6 +1590,9 @@ 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); DBUG_RETURN(FALSE); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c152984876e..7cb97d156cb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3852,6 +3852,12 @@ mysql_execute_command(THD *thd) /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ if (create_info.tmp_table()) thd->variables.option_bits|= OPTION_KEEP_LOG; + /* 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); my_ok(thd); } } @@ -4608,6 +4614,14 @@ end_with_restore_list: /* DDL and binlog write order are protected by metadata locks. */ res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table()); + + /* when dropping temporary tables if @@session_track_state_change is ON then + 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); + } break; } case SQLCOM_SHOW_PROCESSLIST: diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 848358e517a..98bc5d606af 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -2781,7 +2781,7 @@ static void update_func_double(THD *thd, struct st_mysql_sys_var *var, ****************************************************************************/ -sys_var *find_sys_var(THD *thd, const char *str, uint length) +sys_var *find_sys_var(THD *thd, const char *str, size_t length) { sys_var *var; sys_var_pluginvar *pi= NULL; diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index efa48b22ce8..96f8411f8ed 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -120,6 +120,8 @@ struct st_plugin_int }; +extern mysql_mutex_t LOCK_plugin; + /* See intern_plugin_lock() for the explanation for the conditionally defined plugin_ref type diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index e8a7dce5771..cc41bd6284e 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2758,7 +2758,11 @@ void mysql_sql_stmt_prepare(THD *thd) thd->stmt_map.erase(stmt); } else + { + thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, + NULL); my_ok(thd, 0L, 0L, "Statement prepared"); + } DBUG_VOID_RETURN; } @@ -3208,6 +3212,8 @@ void mysql_sql_stmt_close(THD *thd) else { stmt->deallocate(); + thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, + NULL); my_ok(thd); } } diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 767154e019d..28e7b899133 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -1,4 +1,5 @@ /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2016, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1157,3 +1158,16 @@ uint convert_to_printable(char *to, size_t to_len, *t= '\0'; return t - to; } + +void String::q_net_store_length(ulonglong 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) +{ + q_net_store_length(length); + bool res= append((const char *)from, length); + DBUG_ASSERT(!res); +} diff --git a/sql/sql_string.h b/sql/sql_string.h index 51a11c7a4ff..10f3c4aee43 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -495,6 +495,11 @@ public: { Ptr[str_length++] = c; } + void q_append2b(const uint32 n) + { + int2store(Ptr + str_length, n); + str_length += 2; + } void q_append(const uint32 n) { int4store(Ptr + str_length, n); @@ -559,6 +564,17 @@ public: return Ptr+ old_length; /* Area to use */ } + inline bool prep_alloc(uint32 arg_length, uint32 step_alloc) + { + uint32 new_length= arg_length + str_length; + if (new_length > Alloced_length) + { + if (realloc(new_length + step_alloc)) + return true; + } + return false; + } + inline bool append(const char *s, uint32 arg_length, uint32 step_alloc) { uint32 new_length= arg_length + str_length; @@ -623,6 +639,8 @@ public: { return !sortcmp(this, other, cs); } + void q_net_store_length(ulonglong length); + void q_net_store_data(const uchar *from, size_t length); }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c9194bcb276..bed116a2930 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -55,6 +55,7 @@ #include "transaction.h" #include "sql_audit.h" + #ifdef __WIN__ #include <io.h> #endif @@ -9228,6 +9229,9 @@ 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); } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 4047b5d6781..ea9f1d14eee 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -5374,3 +5374,36 @@ static Sys_var_ulong Sys_log_tc_size( DEFAULT(my_getpagesize() * 6), BLOCK_SIZE(my_getpagesize())); #endif + + +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)); +} + +static Sys_var_mybool Sys_session_track_schema( + "session_track_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_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)); +} + +static Sys_var_mybool Sys_session_track_state_change( + "session_track_state_change", + "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)); |