summaryrefslogtreecommitdiff
path: root/sql/session_tracker.cc
diff options
context:
space:
mode:
authorOleksandr Byelkin <sanja@mariadb.com>2016-05-30 21:22:50 +0200
committerOleksandr Byelkin <sanja@mariadb.com>2016-08-31 17:17:46 +0200
commit0ee3e64c55664332e8e92eda55b43692159fe4fe (patch)
treeb12a876260d383a9ded16593bda2edcc668cbf7c /sql/session_tracker.cc
parentc8948b0d0db4c182a744bc8bdbde7cbccff3d57d (diff)
downloadmariadb-git-0ee3e64c55664332e8e92eda55b43692159fe4fe.tar.gz
MDEV-8931: (server part of) session state tracking
Transaction tracker
Diffstat (limited to 'sql/session_tracker.cc')
-rw-r--r--sql/session_tracker.cc767
1 files changed, 661 insertions, 106 deletions
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