summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt4
-rw-r--r--sql/net_serv.cc1
-rw-r--r--sql/protocol.cc138
-rw-r--r--sql/session_tracker.cc358
-rw-r--r--sql/session_tracker.h159
-rw-r--r--sql/set_var.cc32
-rw-r--r--sql/set_var.h2
-rw-r--r--sql/share/errmsg-utf8.txt5
-rw-r--r--sql/sp_head.cc10
-rw-r--r--sql/sql_class.cc3
-rw-r--r--sql/sql_class.h4
-rw-r--r--sql/sql_db.cc14
-rw-r--r--sql/sql_parse.cc14
-rw-r--r--sql/sql_plugin.cc2
-rw-r--r--sql/sql_plugin.h2
-rw-r--r--sql/sql_prepare.cc6
-rw-r--r--sql/sql_string.cc14
-rw-r--r--sql/sql_string.h18
-rw-r--r--sql/sql_table.cc4
-rw-r--r--sql/sys_vars.cc33
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));