From 8cbc96b2f52fe7c1fde157603bb6c90eb279144c Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Wed, 24 Aug 2016 15:00:59 +1000 Subject: [PATCH 1/8] Markdown README for a prettier github representation --- CMakeLists.txt | 2 +- README => README.md | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) rename README => README.md (88%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ace4084acd..a87d08cd1ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -443,7 +443,7 @@ ADD_CUSTOM_TARGET(INFO_BIN ALL WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) -INSTALL_DOCUMENTATION(README CREDITS COPYING COPYING.LESSER COPYING.thirdparty +INSTALL_DOCUMENTATION(README.md CREDITS COPYING COPYING.LESSER COPYING.thirdparty EXCEPTIONS-CLIENT COMPONENT Readme) # MDEV-6526 these files are not installed anymore #INSTALL_DOCUMENTATION(${CMAKE_BINARY_DIR}/Docs/INFO_SRC diff --git a/README b/README.md similarity index 88% rename from README rename to README.md index a9413f16b04..f34e6a43b71 100644 --- a/README +++ b/README.md @@ -1,3 +1,5 @@ +## MariaDB: drop-in replacement for MySQL + MariaDB is designed as a drop-in replacement of MySQL(R) with more features, new storage engines, fewer bugs, and better performance. @@ -24,10 +26,17 @@ https://mariadb.com/kb/en/mariadb-versus-mysql-compatibility/ As MariaDB is a full replacement of MySQL, the MySQL manual at http://dev.mysql.com/doc is generally applicable. +Help: +----- + More help is available from the Maria Discuss mailing list https://launchpad.net/~maria-discuss and the #maria IRC channel on Freenode. + +License: +-------- + *************************************************************************** NOTE: @@ -42,7 +51,8 @@ and COPYING.thirdparty files. *************************************************************************** -IMPORTANT: +Bug Reports: +------------ Bug and/or error reports regarding MariaDB should be submitted at http://mariadb.org/jira @@ -53,3 +63,8 @@ The code for MariaDB, including all revision history, can be found at: https://github.com/MariaDB/server *************************************************************************** + +Code status: +------------ + +* [![tests status](https://secure.travis-ci.org/MariaDB/server.png?branch=10.2)](https://travis-ci.org/MariaDB/server) travis-ci.org (10.2 branch) From 468a6ad722778768eb4ee5003dd818945b363261 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Wed, 31 Aug 2016 11:48:51 +0400 Subject: [PATCH 2/8] Fixed package build failure README -> README.md --- debian/mariadb-client-10.2.docs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/mariadb-client-10.2.docs b/debian/mariadb-client-10.2.docs index 21446855f51..8117d689011 100644 --- a/debian/mariadb-client-10.2.docs +++ b/debian/mariadb-client-10.2.docs @@ -1,2 +1,2 @@ debian/additions/innotop/changelog.innotop -README +README.md From e7608a78ef45cc46f4e4d5abbda788ad54e80e71 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Fri, 15 Apr 2016 20:40:25 +0200 Subject: [PATCH 3/8] MDEV-8931: (server part of) session state tracking initial commit to test --- include/mysql.h.pp | 9 + include/mysql_com.h | 53 ++- libmysqld/CMakeLists.txt | 1 + libmysqld/lib_sql.cc | 3 +- mysql-test/r/mysqld--help.result | 7 + .../sys_vars/r/sysvars_server_embedded.result | 28 ++ .../r/sysvars_server_notembedded.result | 28 ++ sql-common/pack.c | 22 ++ sql/CMakeLists.txt | 4 +- sql/net_serv.cc | 1 - sql/protocol.cc | 138 +++++-- sql/session_tracker.cc | 358 ++++++++++++++++++ sql/session_tracker.h | 159 ++++++++ sql/set_var.cc | 32 +- sql/set_var.h | 2 +- sql/share/errmsg-utf8.txt | 5 +- sql/sp_head.cc | 10 + sql/sql_class.cc | 3 + sql/sql_class.h | 4 + sql/sql_db.cc | 14 +- sql/sql_parse.cc | 14 + sql/sql_plugin.cc | 2 +- sql/sql_plugin.h | 2 + sql/sql_prepare.cc | 6 + sql/sql_string.cc | 14 + sql/sql_string.h | 18 + sql/sql_table.cc | 4 + sql/sys_vars.cc | 33 ++ 28 files changed, 928 insertions(+), 46 deletions(-) create mode 100644 sql/session_tracker.cc create mode 100644 sql/session_tracker.h diff --git a/include/mysql.h.pp b/include/mysql.h.pp index b9018376876..57ce4f78d99 100644 --- a/include/mysql.h.pp +++ b/include/mysql.h.pp @@ -87,6 +87,15 @@ enum enum_mysql_set_option MYSQL_OPTION_MULTI_STATEMENTS_ON, MYSQL_OPTION_MULTI_STATEMENTS_OFF }; +enum enum_session_state_type +{ + SESSION_TRACK_SYSTEM_VARIABLES, + SESSION_TRACK_SCHEMA, + SESSION_TRACK_STATE_CHANGE, + SESSION_TRACK_GTIDS, + SESSION_TRACK_TRANSACTION_CHARACTERISTICS, + SESSION_TRACK_TRANSACTION_STATE +}; my_bool my_net_init(NET *net, Vio* vio, void *thd, unsigned int my_flags); void my_net_local_init(NET *net); void net_end(NET *net); diff --git a/include/mysql_com.h b/include/mysql_com.h index 8e7bf2337cc..9eb0e4f2d74 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -72,6 +72,14 @@ #define INDEX_COMMENT_MAXLEN 1024 #define TABLE_PARTITION_COMMENT_MAXLEN 1024 +/* + Maximum length of protocol packet. + OK packet length limit also restricted to this value as any length greater + than this value will have first byte of OK packet to be 254 thus does not + provide a means to identify if this is OK or EOF packet. +*/ +#define MAX_PACKET_LENGTH (256L*256L*256L-1) + /* USER_HOST_BUFF_SIZE -- length of string buffer, that is enough to contain username and hostname parts of the user identifier with trailing zero in @@ -221,6 +229,14 @@ enum enum_server_command /* Don't close the connection for a connection with expired password. */ #define CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS (1UL << 22) +/** + Capable of handling server state change information. Its a hint to the + server to include the state change information in Ok packet. +*/ +#define CLIENT_SESSION_TRACK (1UL << 23) +/* Client no longer needs EOF packet */ +#define CLIENT_DEPRECATE_EOF (1UL << 24) + #define CLIENT_PROGRESS_OBSOLETE (1UL << 29) #define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30) /* @@ -272,10 +288,12 @@ enum enum_server_command CLIENT_MULTI_RESULTS | \ CLIENT_PS_MULTI_RESULTS | \ CLIENT_SSL_VERIFY_SERVER_CERT | \ - CLIENT_REMEMBER_OPTIONS | \ + CLIENT_REMEMBER_OPTIONS | \ MARIADB_CLIENT_PROGRESS | \ CLIENT_PLUGIN_AUTH | \ CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | \ + CLIENT_SESSION_TRACK |\ + CLIENT_DEPRECATE_EOF |\ CLIENT_CONNECT_ATTRS |\ MARIADB_CLIENT_COM_MULTI) @@ -340,6 +358,11 @@ enum enum_server_command */ #define SERVER_STATUS_IN_TRANS_READONLY 8192 +/** + This status flag, when on, implies that one of the state information has + changed on the server because of the execution of the last statement. +*/ +#define SERVER_SESSION_STATE_CHANGED (1UL << 14) /** Server status flags that must be cleared when starting @@ -356,7 +379,8 @@ enum enum_server_command SERVER_QUERY_WAS_SLOW |\ SERVER_STATUS_DB_DROPPED |\ SERVER_STATUS_CURSOR_EXISTS|\ - SERVER_STATUS_LAST_ROW_SENT) + SERVER_STATUS_LAST_ROW_SENT|\ + SERVER_SESSION_STATE_CHANGED) #define MYSQL_ERRMSG_SIZE 512 #define NET_READ_TIMEOUT 30 /* Timeout on read */ @@ -523,6 +547,30 @@ enum enum_mysql_set_option MYSQL_OPTION_MULTI_STATEMENTS_OFF }; +/* + Type of state change information that the server can include in the Ok + packet. + Note : 1) session_state_type shouldn't go past 255 (i.e. 1-byte boundary). + 2) Modify the definition of SESSION_TRACK_END when a new member is + added. +*/ +enum enum_session_state_type +{ + SESSION_TRACK_SYSTEM_VARIABLES, /* Session system variables */ + SESSION_TRACK_SCHEMA, /* Current schema */ + SESSION_TRACK_STATE_CHANGE, /* track session state changes */ + SESSION_TRACK_GTIDS, + SESSION_TRACK_TRANSACTION_CHARACTERISTICS, /* Transaction chistics */ + SESSION_TRACK_TRANSACTION_STATE /* Transaction state */ +}; + +#define SESSION_TRACK_BEGIN SESSION_TRACK_SYSTEM_VARIABLES + +#define SESSION_TRACK_END SESSION_TRACK_TRANSACTION_STATE + +#define IS_SESSION_STATE_TYPE(T) \ + (((int)(T) >= SESSION_TRACK_BEGIN) && ((T) <= SESSION_TRACK_END)) + #define net_new_transaction(net) ((net)->pkt_nr=0) #ifdef __cplusplus @@ -641,6 +689,7 @@ my_ulonglong net_field_length_ll(uchar **packet); my_ulonglong safe_net_field_length_ll(uchar **packet, size_t packet_len); uchar *net_store_length(uchar *pkg, ulonglong length); uchar *safe_net_store_length(uchar *pkg, size_t pkg_len, ulonglong length); +unsigned int net_length_size(ulonglong num); #endif #ifdef __cplusplus diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 368f30f8317..4518329a3dd 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -112,6 +112,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/item_windowfunc.cc ../sql/sql_window.cc ../sql/sql_cte.cc ../sql/temporary_tables.cc + ../sql/session_tracker.cc ${GEN_SOURCES} ${MYSYS_LIBWRAP_SOURCE} ) diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 4a84e63f222..8c01fc8b9b6 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -1172,7 +1172,8 @@ bool net_send_ok(THD *thd, uint server_status, uint statement_warn_count, ulonglong affected_rows, ulonglong id, const char *message, - bool unused __attribute__((unused))) + bool unused1 __attribute__((unused)), + bool unused2 __attribute__((unused))) { DBUG_ENTER("emb_net_send_ok"); MYSQL_DATA *data; diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index 0d52a50eb56..8e82635ec91 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -903,6 +903,11 @@ The following options may be given as the first argument: files within specified directory --server-id=# Uniquely identifies the server instance in the community of replication partners + --session-track-schema + Track changes to the 'default schema'. + (Defaults to on; use --skip-session-track-schema to disable.) + --session-track-state-change + Track changes to the 'session state'. --show-slave-auth-info Show user and password in SHOW SLAVE HOSTS on this master. @@ -1385,6 +1390,8 @@ safe-user-create FALSE secure-auth TRUE secure-file-priv (No default value) server-id 1 +session-track-schema TRUE +session-track-state-change FALSE show-slave-auth-info FALSE silent-startup FALSE skip-grant-tables TRUE diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index eecebce80ad..13733c38a94 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -3327,6 +3327,34 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME SESSION_TRACK_SCHEMA +SESSION_VALUE ON +GLOBAL_VALUE ON +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE ON +VARIABLE_SCOPE SESSION +VARIABLE_TYPE BOOLEAN +VARIABLE_COMMENT Track changes to the 'default schema'. +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST OFF,ON +READ_ONLY NO +COMMAND_LINE_ARGUMENT OPTIONAL +VARIABLE_NAME SESSION_TRACK_STATE_CHANGE +SESSION_VALUE OFF +GLOBAL_VALUE OFF +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE OFF +VARIABLE_SCOPE SESSION +VARIABLE_TYPE BOOLEAN +VARIABLE_COMMENT Track changes to the 'session state'. +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST OFF,ON +READ_ONLY NO +COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME SKIP_EXTERNAL_LOCKING SESSION_VALUE NULL GLOBAL_VALUE ON diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 4e98bc9f102..e7f953e5f4c 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -3789,6 +3789,34 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME SESSION_TRACK_SCHEMA +SESSION_VALUE ON +GLOBAL_VALUE ON +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE ON +VARIABLE_SCOPE SESSION +VARIABLE_TYPE BOOLEAN +VARIABLE_COMMENT Track changes to the 'default schema'. +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST OFF,ON +READ_ONLY NO +COMMAND_LINE_ARGUMENT OPTIONAL +VARIABLE_NAME SESSION_TRACK_STATE_CHANGE +SESSION_VALUE OFF +GLOBAL_VALUE OFF +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE OFF +VARIABLE_SCOPE SESSION +VARIABLE_TYPE BOOLEAN +VARIABLE_COMMENT Track changes to the 'session state'. +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST OFF,ON +READ_ONLY NO +COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME SKIP_EXTERNAL_LOCKING SESSION_VALUE NULL GLOBAL_VALUE ON diff --git a/sql-common/pack.c b/sql-common/pack.c index 4bb4a0b7a4e..5428feb623e 100644 --- a/sql-common/pack.c +++ b/sql-common/pack.c @@ -186,3 +186,25 @@ uchar *safe_net_store_length(uchar *packet, size_t packet_len, ulonglong length) return packet+8; } + +/** + The length of space required to store the resulting length-encoded integer + for the given number. This function can be used at places where one needs to + dynamically allocate the buffer for a given number to be stored as length- + encoded integer. + + @param num [IN] the input number + + @return length of buffer needed to store this number [1, 3, 4, 9]. +*/ + +uint net_length_size(ulonglong num) +{ + if (num < (ulonglong) 251LL) + return 1; + if (num < (ulonglong) 65536LL) + return 3; + if (num < (ulonglong) 16777216LL) + return 4; + return 9; +} 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 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 *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 *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 *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 *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 *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 #include #include +#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 #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)); From c8948b0d0db4c182a744bc8bdbde7cbccff3d57d Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Fri, 15 Apr 2016 20:47:45 +0200 Subject: [PATCH 4/8] MDEV-8931: (server part of) session state tracking System variables tracking --- include/mysql_com.h | 8 +- mysql-test/r/mysqld--help.result | 3 + ...ession_track_system_variables_basic.result | 162 ++++ .../r/sysvars_server_notembedded.result | 14 + .../session_track_system_variables_basic.test | 133 +++ sql/mysqld.cc | 20 +- sql/mysqld.h | 1 + sql/protocol.cc | 1 - sql/session_tracker.cc | 769 +++++++++++++++++- sql/session_tracker.h | 22 +- sql/set_var.cc | 31 +- sql/set_var.h | 7 + sql/share/errmsg-utf8.txt | 30 +- sql/sql_class.cc | 4 + sql/sql_class.h | 2 + sql/sql_plugin.cc | 54 +- sql/sql_plugin.h | 2 + sql/sql_show.cc | 230 +++--- sql/sql_show.h | 6 + sql/sql_string.h | 4 +- sql/sys_vars.cc | 10 + sql/sys_vars.ic | 129 ++- 22 files changed, 1486 insertions(+), 156 deletions(-) create mode 100644 mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result create mode 100644 mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test diff --git a/include/mysql_com.h b/include/mysql_com.h index 9eb0e4f2d74..7433411f29a 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -288,7 +288,7 @@ enum enum_server_command CLIENT_MULTI_RESULTS | \ CLIENT_PS_MULTI_RESULTS | \ CLIENT_SSL_VERIFY_SERVER_CERT | \ - CLIENT_REMEMBER_OPTIONS | \ + CLIENT_REMEMBER_OPTIONS | \ MARIADB_CLIENT_PROGRESS | \ CLIENT_PLUGIN_AUTH | \ CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | \ @@ -556,9 +556,9 @@ enum enum_mysql_set_option */ enum enum_session_state_type { - SESSION_TRACK_SYSTEM_VARIABLES, /* Session system variables */ - SESSION_TRACK_SCHEMA, /* Current schema */ - SESSION_TRACK_STATE_CHANGE, /* track session state changes */ + SESSION_TRACK_SYSTEM_VARIABLES, /* Session system variables */ + SESSION_TRACK_SCHEMA, /* Current schema */ + SESSION_TRACK_STATE_CHANGE, /* track session state changes */ SESSION_TRACK_GTIDS, SESSION_TRACK_TRANSACTION_CHARACTERISTICS, /* Transaction chistics */ SESSION_TRACK_TRANSACTION_STATE /* Transaction state */ diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index 8e82635ec91..7fc9fedd456 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -908,6 +908,8 @@ The following options may be given as the first argument: (Defaults to on; use --skip-session-track-schema to disable.) --session-track-state-change Track changes to the 'session state'. + --session-track-system-variables=name + Track changes in registered system variables. --show-slave-auth-info Show user and password in SHOW SLAVE HOSTS on this master. @@ -1392,6 +1394,7 @@ secure-file-priv (No default value) server-id 1 session-track-schema TRUE session-track-state-change FALSE +session-track-system-variables autocommit,character_set_client,character_set_connection,character_set_results,time_zone show-slave-auth-info FALSE silent-startup FALSE skip-grant-tables TRUE diff --git a/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result b/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result new file mode 100644 index 00000000000..e451a22d322 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result @@ -0,0 +1,162 @@ +# +# Variable name : session_track_system_variables +# Scope : Global & Session +# +# Global - default +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +autocommit,character_set_client,character_set_connection,character_set_results,time_zone +# Session - default +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +autocommit,character_set_client,character_set_connection,character_set_results,time_zone + +# via INFORMATION_SCHEMA.GLOBAL_VARIABLES +SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; +VARIABLE_NAME VARIABLE_VALUE +SESSION_TRACK_SCHEMA ON +SESSION_TRACK_STATE_CHANGE OFF +SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,time_zone +# via INFORMATION_SCHEMA.SESSION_VARIABLES +SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; +VARIABLE_NAME VARIABLE_VALUE +SESSION_TRACK_SCHEMA ON +SESSION_TRACK_STATE_CHANGE OFF +SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SET @global_saved_tmp = @@global.session_track_system_variables; + +# Altering global variable's value +SET @@global.session_track_system_variables='autocommit'; +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +autocommit +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +autocommit,character_set_client,character_set_connection,character_set_results,time_zone + +# Altering session variable's value +SET @@session.session_track_system_variables='autocommit'; +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +autocommit +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +autocommit + +# Variables' values in a new session. +connect con1,"127.0.0.1",root,,test,$MASTER_MYPORT,; +# Global - expect "autocommit" +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +autocommit + +# Session - expect "autocommit" +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +autocommit + +# Switching to the default connection. +connection default; +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +autocommit +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +autocommit + +# Test if DEFAULT is working as expected. +SET @@global.session_track_system_variables = DEFAULT; +SET @@session.session_track_system_variables = DEFAULT; + +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +autocommit,character_set_client,character_set_connection,character_set_results,time_zone + +# Variables' values in a new session (con2). +connect con2,"127.0.0.1",root,,test,$MASTER_MYPORT,; +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +autocommit,character_set_client,character_set_connection,character_set_results,time_zone + +# Altering session should not affect global. +SET @@session.session_track_system_variables = 'sql_mode'; +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +sql_mode + +# Variables' values in a new session (con3). +connect con3,"127.0.0.1",root,,test,$MASTER_MYPORT,; +# Altering global should not affect session. +SET @@global.session_track_system_variables = 'sql_mode'; +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +sql_mode +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +autocommit,character_set_client,character_set_connection,character_set_results,time_zone + +# Switching to the default connection. +connection default; +# Testing NULL +SET @@global.session_track_system_variables = NULL; +SET @@session.session_track_system_variables = NULL; +# Global - expect "" instead of NULL +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +NULL +# Session - expect "" instead of NULL +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables + +# testing with duplicate entries. +SET @@global.session_track_system_variables= "time_zone"; +SET @@session.session_track_system_variables= "time_zone"; +SET @@global.session_track_system_variables= "sql_mode,sql_mode"; +SET @@session.session_track_system_variables= "sql_mode,sql_mode"; +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +sql_mode +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +sql_mode + +# testing ordering +SET @@global.session_track_system_variables= "time_zone,sql_mode"; +SET @@session.session_track_system_variables= "time_zone,sql_mode"; +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +sql_mode,time_zone +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +sql_mode,time_zone + +# special values +SET @@global.session_track_system_variables= "*"; +SET @@session.session_track_system_variables= "*"; +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables +* +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables +* +SET @@global.session_track_system_variables= ""; +SET @@session.session_track_system_variables= ""; +SELECT @@global.session_track_system_variables; +@@global.session_track_system_variables + +SELECT @@session.session_track_system_variables; +@@session.session_track_system_variables + + +# Restoring the original values. +SET @@global.session_track_system_variables = @global_saved_tmp; +# End of tests. diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index e7f953e5f4c..c0d07280253 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -3817,6 +3817,20 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST OFF,ON READ_ONLY NO COMMAND_LINE_ARGUMENT OPTIONAL +VARIABLE_NAME SESSION_TRACK_SYSTEM_VARIABLES +SESSION_VALUE autocommit,character_set_client,character_set_connection,character_set_results,time_zone +GLOBAL_VALUE autocommit,character_set_client,character_set_connection,character_set_results,time_zone +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE autocommit,character_set_client,character_set_connection,character_set_results,time_zone +VARIABLE_SCOPE SESSION +VARIABLE_TYPE VARCHAR +VARIABLE_COMMENT Track changes in registered system variables. +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME SKIP_EXTERNAL_LOCKING SESSION_VALUE NULL GLOBAL_VALUE ON diff --git a/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test b/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test new file mode 100644 index 00000000000..bbb32bb67a3 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test @@ -0,0 +1,133 @@ +--source include/not_embedded.inc + +--echo # +--echo # Variable name : session_track_system_variables +--echo # Scope : Global & Session +--echo # + +--echo # Global - default +SELECT @@global.session_track_system_variables; +--echo # Session - default +SELECT @@session.session_track_system_variables; +--echo + +--echo # via INFORMATION_SCHEMA.GLOBAL_VARIABLES +--disable_warnings +SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; +--enable_warnings + +--echo # via INFORMATION_SCHEMA.SESSION_VARIABLES +--disable_warnings +SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; +--enable_warnings + +# Save the global value to be used to restore the original value. +SET @global_saved_tmp = @@global.session_track_system_variables; +--echo + +--echo # Altering global variable's value +SET @@global.session_track_system_variables='autocommit'; +SELECT @@global.session_track_system_variables; +SELECT @@session.session_track_system_variables; +--echo + +--echo # Altering session variable's value +SET @@session.session_track_system_variables='autocommit'; +SELECT @@global.session_track_system_variables; +SELECT @@session.session_track_system_variables; +--echo + +--echo # Variables' values in a new session. +connect (con1,"127.0.0.1",root,,test,$MASTER_MYPORT,); + +--echo # Global - expect "autocommit" +SELECT @@global.session_track_system_variables; +--echo +--echo # Session - expect "autocommit" +SELECT @@session.session_track_system_variables; +--echo + +--echo # Switching to the default connection. +connection default; + +SELECT @@global.session_track_system_variables; +SELECT @@session.session_track_system_variables; +--echo + +--echo # Test if DEFAULT is working as expected. +SET @@global.session_track_system_variables = DEFAULT; +SET @@session.session_track_system_variables = DEFAULT; +--echo + +SELECT @@global.session_track_system_variables; +SELECT @@session.session_track_system_variables; +--echo + +--echo # Variables' values in a new session (con2). +connect (con2,"127.0.0.1",root,,test,$MASTER_MYPORT,); + +SELECT @@global.session_track_system_variables; +SELECT @@session.session_track_system_variables; +--echo + +--echo # Altering session should not affect global. +SET @@session.session_track_system_variables = 'sql_mode'; +SELECT @@global.session_track_system_variables; +SELECT @@session.session_track_system_variables; +--echo + +--echo # Variables' values in a new session (con3). +connect (con3,"127.0.0.1",root,,test,$MASTER_MYPORT,); + +--echo # Altering global should not affect session. +SET @@global.session_track_system_variables = 'sql_mode'; +SELECT @@global.session_track_system_variables; +SELECT @@session.session_track_system_variables; +--echo + +--echo # Switching to the default connection. +connection default; + +--echo # Testing NULL +SET @@global.session_track_system_variables = NULL; +SET @@session.session_track_system_variables = NULL; + +--echo # Global - expect "" instead of NULL +SELECT @@global.session_track_system_variables; +--echo # Session - expect "" instead of NULL +SELECT @@session.session_track_system_variables; + +--echo # testing with duplicate entries. +# Lets first set it to some valid value. +SET @@global.session_track_system_variables= "time_zone"; +SET @@session.session_track_system_variables= "time_zone"; +# Now set with duplicate entries (must pass) +SET @@global.session_track_system_variables= "sql_mode,sql_mode"; +SET @@session.session_track_system_variables= "sql_mode,sql_mode"; +SELECT @@global.session_track_system_variables; +SELECT @@session.session_track_system_variables; +--echo + +--echo # testing ordering +SET @@global.session_track_system_variables= "time_zone,sql_mode"; +SET @@session.session_track_system_variables= "time_zone,sql_mode"; +SELECT @@global.session_track_system_variables; +SELECT @@session.session_track_system_variables; +--echo + +--echo # special values +SET @@global.session_track_system_variables= "*"; +SET @@session.session_track_system_variables= "*"; +SELECT @@global.session_track_system_variables; +SELECT @@session.session_track_system_variables; +SET @@global.session_track_system_variables= ""; +SET @@session.session_track_system_variables= ""; +SELECT @@global.session_track_system_variables; +SELECT @@session.session_track_system_variables; +--echo + + +--echo # Restoring the original values. +SET @@global.session_track_system_variables = @global_saved_tmp; + +--echo # End of tests. diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8fa8f01b894..b59c6c7048f 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -690,6 +690,14 @@ THD *next_global_thread(THD *thd) } struct system_variables global_system_variables; +/** + Following is just for options parsing, used with a difference against + global_system_variables. + + TODO: something should be done to get rid of following variables +*/ +const char *current_dbug_option=""; + struct system_variables max_system_variables; struct system_status_var global_status_var; @@ -1463,7 +1471,6 @@ my_bool plugins_are_initialized= FALSE; #ifndef DBUG_OFF static const char* default_dbug_option; #endif -const char *current_dbug_option=""; #ifdef HAVE_LIBWRAP const char *libwrapName= NULL; int allow_severity = LOG_INFO; @@ -5278,6 +5285,17 @@ static int init_server_components() } plugins_are_initialized= TRUE; /* Don't separate from init function */ + { + Session_tracker session_track_system_variables_check; + if (session_track_system_variables_check. + server_boot_verify(system_charset_info)) + { + sql_print_error("The variable session_track_system_variables has " + "invalid values."); + unireg_abort(1); + } + } + /* we do want to exit if there are any other unknown options */ if (remaining_argc > 1) { diff --git a/sql/mysqld.h b/sql/mysqld.h index 846a01a9427..68eab815564 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -135,6 +135,7 @@ extern my_bool lower_case_file_system; extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs; extern my_bool opt_secure_auth; extern const char *current_dbug_option; +extern const char *current_session_track_system_variables; extern char* opt_secure_file_priv; extern char* opt_secure_backup_file_priv; extern size_t opt_secure_backup_file_priv_len; diff --git a/sql/protocol.cc b/sql/protocol.cc index e12c72dd988..77dedfbc7d2 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -276,7 +276,6 @@ net_send_ok(THD *thd, /* 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); } diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc index ad9906d7159..cfbb1704318 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 */ +#include "sql_plugin.h" #include "session_tracker.h" #include "hash.h" @@ -23,6 +24,7 @@ #include "sql_class.h" #include "sql_show.h" #include "sql_plugin.h" +#include "set_var.h" class Not_implemented_tracker : public State_tracker { @@ -40,6 +42,182 @@ public: }; +static my_bool name_array_filler(void *ptr, void *data_ptr); +/** + Session_sysvars_tracker + + This is a tracker class that enables & manages the tracking of session + system variables. It internally maintains a hash of user supplied variable + references and a boolean field to store if the variable was changed by the + last statement. +*/ + +class Session_sysvars_tracker : public State_tracker +{ +private: + + struct sysvar_node_st { + sys_var *m_svar; + bool *test_load; + bool m_changed; + }; + + class vars_list + { + private: + /** + Registered system variables. (@@session_track_system_variables) + A hash to store the name of all the system variables specified by the + user. + */ + HASH m_registered_sysvars; + /** Size of buffer for string representation */ + size_t buffer_length; + myf m_mem_flag; + /** + If TRUE then we want to check all session variable. + */ + bool track_all; + void init() + { + my_hash_init(&m_registered_sysvars, + &my_charset_bin, + 4, 0, 0, (my_hash_get_key) sysvars_get_key, + my_free, MYF(HASH_UNIQUE | + ((m_mem_flag & MY_THREAD_SPECIFIC) ? + HASH_THREAD_SPECIFIC : 0))); + } + void free_hash() + { + if (my_hash_inited(&m_registered_sysvars)) + { + my_hash_free(&m_registered_sysvars); + } + } + + uchar* search(const sys_var *svar) + { + return (my_hash_search(&m_registered_sysvars, (const uchar *)&svar, + sizeof(sys_var *))); + } + + public: + vars_list() : + buffer_length(0) + { + m_mem_flag= current_thd ? MY_THREAD_SPECIFIC : 0; + init(); + } + + size_t get_buffer_length() + { + DBUG_ASSERT(buffer_length != 0); // asked earlier then should + return buffer_length; + } + ~vars_list() + { + /* free the allocated hash. */ + if (my_hash_inited(&m_registered_sysvars)) + { + my_hash_free(&m_registered_sysvars); + } + } + + uchar* search(sysvar_node_st *node, const sys_var *svar) + { + uchar *res; + res= search(svar); + if (!res) + { + if (track_all) + { + insert(node, svar, m_mem_flag); + return search(svar); + } + } + return res; + } + + uchar* operator[](ulong idx) + { + return my_hash_element(&m_registered_sysvars, idx); + } + bool insert(sysvar_node_st *node, const sys_var *svar, myf mem_flag); + 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); + bool construct_var_list(char *buf, size_t buf_len); + }; + /** + Two objects of vars_list type are maintained to manage + various operations. + */ + vars_list *orig_list, *tool_list; + +public: + Session_sysvars_tracker() + { + orig_list= new (std::nothrow) vars_list(); + tool_list= new (std::nothrow) vars_list(); + } + + ~Session_sysvars_tracker() + { + if (orig_list) + delete orig_list; + if (tool_list) + delete tool_list; + } + + size_t get_buffer_length() + { + return orig_list->get_buffer_length(); + } + bool construct_var_list(char *buf, size_t buf_len) + { + return orig_list->construct_var_list(buf, buf_len); + } + + /** + Method used to check the validity of string provided + for session_track_system_variables during the server + startup. + */ + static bool server_init_check(THD *thd, const 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; + } + static bool server_init_process(THD *thd, const 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); + if (!result) + dummy.construct_var_list(var_list.str, var_list.length + 1); + return result; + } + + 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 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); +}; + + /** Current_schema_tracker, @@ -108,6 +286,540 @@ public: /* To be used in expanding the buffer. */ static const unsigned int EXTRA_ALLOC= 1024; + +void Session_sysvars_tracker::vars_list::reset() +{ + buffer_length= 0; + track_all= 0; + if (m_registered_sysvars.records) + my_hash_reset(&m_registered_sysvars); +} + +/** + Copy the given list. + + @param from Source vars_list object. + @param thd THD handle to retrive the charset in use. + + @retval true there is something to track + @retval false nothing to track +*/ + +void Session_sysvars_tracker::vars_list::copy(vars_list* from, THD *thd) +{ + reset(); + track_all= from->track_all; + free_hash(); + buffer_length= from->buffer_length; + m_registered_sysvars= from->m_registered_sysvars; + from->init(); +} + +/** + Inserts the variable to be tracked into m_registered_sysvars hash. + + @param node Node to be inserted. + @param svar address of the system variable + + @retval false success + @retval true error +*/ + +bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node, + const sys_var *svar, + myf mem_flag) +{ + if (!node) + { + if (!(node= (sysvar_node_st *) my_malloc(sizeof(sysvar_node_st), + MYF(MY_WME | mem_flag)))) + { + reset(); + return true; + } + } + + node->m_svar= (sys_var *)svar; + node->test_load= node->m_svar->test_load; + node->m_changed= false; + if (my_hash_insert(&m_registered_sysvars, (uchar *) node)) + { + my_free(node); + if (!search((sys_var *)svar)) + { + //EOF (error is already reported) + reset(); + return true; + } + } + return false; +} + +/** + Parse the specified system variables list. + + @Note In case of invalid entry a warning is raised per invalid entry. + This is done in order to handle 'potentially' valid system + variables from uninstalled plugins which might get installed in + future. + + + @param thd [IN] The thd handle. + @param var_list [IN] System variable list. + @param throw_error [IN] bool when set to true, returns an error + in case of invalid/duplicate values. + @param char_set [IN] charecter set information used for string + manipulations. + @param session_created [IN] bool variable which says if the parse is + already executed once. The mutex on variables + is not acquired if this variable is false. + + @return + true Error + false Success +*/ +bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, + LEX_STRING var_list, + bool throw_error, + const 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) + { + buffer_length= 1; + return false; + } + + if(!strcmp(var_list.str,(const char *)"*")) + { + track_all= true; + buffer_length= 2; + return false; + } + + buffer_length= var_list.length + 1; + token= var_list.str; + + track_all= false; + /* + 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 (insert(NULL, svar, m_mem_flag) == TRUE) + goto error; + } + else 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 + goto error; + + if (lasts) + token= lasts + 1; + else + break; + } + if (!thd || session_created) + mysql_mutex_unlock(&LOCK_plugin); + + return false; + +error: + if (!thd || session_created) + mysql_mutex_unlock(&LOCK_plugin); + return true; +} + +struct name_array_filler_data +{ + LEX_CSTRING **names; + uint idx; + +}; + +/** Collects variable references into array */ +static my_bool name_array_filler(void *ptr, void *data_ptr) +{ + Session_sysvars_tracker::sysvar_node_st *node= + (Session_sysvars_tracker::sysvar_node_st *)ptr; + name_array_filler_data *data= (struct name_array_filler_data *)data_ptr; + if (*node->test_load) + data->names[data->idx++]= &node->m_svar->name; + return FALSE; +} + +/* Sorts variable references array */ +static int name_array_sorter(const void *a, const void *b) +{ + LEX_CSTRING **an= (LEX_CSTRING **)a, **bn=(LEX_CSTRING **)b; + size_t min= MY_MIN((*an)->length, (*bn)->length); + int res= strncmp((*an)->str, (*bn)->str, min); + if (res == 0) + res= ((int)(*bn)->length)- ((int)(*an)->length); + return res; +} + +/** + Construct variable list by internal hash with references +*/ + +bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf, + size_t buf_len) +{ + struct name_array_filler_data data; + size_t left= buf_len; + size_t names_size= m_registered_sysvars.records * sizeof(LEX_CSTRING *); + const char separator= ','; + + if (unlikely(buf_len < 1)) + return true; + + if (unlikely(track_all)) + { + if (buf_len < 2) + return true; + buf[0]= '*'; + buf[1]= '\0'; + return false; + } + + if (m_registered_sysvars.records == 0) + { + buf[0]= '\0'; + return false; + } + + data.names= (LEX_CSTRING**)my_safe_alloca(names_size); + + if (unlikely(!data.names)) + return true; + + data.idx= 0; + + mysql_mutex_lock(&LOCK_plugin); + my_hash_iterate(&m_registered_sysvars, &name_array_filler, &data); + DBUG_ASSERT(data.idx <= m_registered_sysvars.records); + + + if (m_registered_sysvars.records == 0) + { + mysql_mutex_unlock(&LOCK_plugin); + buf[0]= '\0'; + return false; + } + + my_qsort(data.names, data.idx, sizeof(LEX_CSTRING *), + &name_array_sorter); + + for(uint i= 0; i < data.idx; i++) + { + LEX_CSTRING *nm= data.names[i]; + size_t ln= nm->length + 1; + if (ln > left) + { + mysql_mutex_unlock(&LOCK_plugin); + my_safe_afree(data.names, names_size); + return true; + } + memcpy(buf, nm->str, nm->length); + buf[nm->length]= separator; + buf+= ln; + left-= ln; + } + mysql_mutex_unlock(&LOCK_plugin); + + buf--; buf[0]= '\0'; + my_safe_afree(data.names, names_size); + + return false; +} + +/** + Enable session tracker by parsing global value of tracked variables. + + @param thd [IN] The thd handle. + + @retval true Error + @retval false Success +*/ + +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, + true, thd->charset(), false) == true) + { + mysql_mutex_unlock(&LOCK_plugin); + return true; + } + mysql_mutex_unlock(&LOCK_plugin); + orig_list->copy(tool_list, thd); + m_enabled= true; + + return false; +} + + +/** + Check system variable name(s). + + @note This function is called from the ON_CHECK() function of the + session_track_system_variables' sys_var class. + + @param thd [IN] The thd handle. + @param var [IN] A pointer to set_var holding the specified list of + system variable names. + + @retval true Error + @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) +{ + tool_list->reset(); + return tool_list->parse_var_list(thd, val, true, + thd->charset(), true); +} + + +/** + Once the value of the @@session_track_system_variables has been + successfully updated, this function calls + Session_sysvars_tracker::vars_list::copy updating the hash in orig_list + which represents the system variables to be tracked. + + @note This function is called from the ON_UPDATE() function of the + session_track_system_variables' sys_var class. + + @param thd [IN] The thd handle. + + @retval true Error + @retval false Success +*/ + +bool Session_sysvars_tracker::update(THD *thd) +{ + orig_list->copy(tool_list, thd); + return false; +} + + +/** + Store the data for changed system variables 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 true Error + @retval false Success +*/ + +bool Session_sysvars_tracker::store(THD *thd, String *buf) +{ + char val_buf[SHOW_VAR_FUNC_BUFF_SIZE]; + SHOW_VAR show; + const char *value; + sysvar_node_st *node; + const CHARSET_INFO *charset; + size_t val_length, length; + int idx= 0; + + /* As its always system variable. */ + show.type= SHOW_SYS; + + while ((node= (sysvar_node_st *) (*orig_list)[idx])) + { + if (node->m_changed) + { + mysql_mutex_lock(&LOCK_plugin); + if (!*node->test_load) + { + mysql_mutex_unlock(&LOCK_plugin); + continue; + } + sys_var *svar= node->m_svar; + show.name= svar->name.str; + show.value= (char *) svar; + + value= get_one_variable(thd, &show, OPT_SESSION, SHOW_SYS, NULL, + &charset, val_buf, &val_length); + mysql_mutex_unlock(&LOCK_plugin); + + length= net_length_size(svar->name.length) + + svar->name.length + + net_length_size(val_length) + + val_length; + + compile_time_assert(SESSION_TRACK_SYSTEM_VARIABLES < 251); + buf->prep_alloc(1 + net_length_size(length) + length, EXTRA_ALLOC); + + /* Session state type (SESSION_TRACK_SYSTEM_VARIABLES) */ + buf->q_net_store_length((ulonglong)SESSION_TRACK_SYSTEM_VARIABLES); + + /* Length of the overall entity. */ + buf->q_net_store_length((ulonglong)length); + + /* System variable's name (length-encoded string). */ + buf->q_net_store_data((const uchar*)svar->name.str, svar->name.length); + + /* System variable's value (length-encoded string). */ + buf->q_net_store_data((const uchar*)value, val_length); + } + ++ idx; + } + + reset(); + + return false; +} + + +/** + Mark the system variable as changed. + + @param [IN] pointer on a variable +*/ + +void Session_sysvars_tracker::mark_as_changed(THD *thd, + LEX_CSTRING *var) +{ + sysvar_node_st *node= NULL; + sys_var *svar= (sys_var *)var; + /* + 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)))) + { + 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; + } +} + + +/** + Supply key to a hash. + + @param entry [IN] A single entry. + @param length [OUT] Length of the key. + @param not_used Unused. + + @return Pointer to the key buffer. +*/ + +uchar *Session_sysvars_tracker::sysvars_get_key(const char *entry, + size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length= sizeof(sys_var *); + return (uchar *) &(((sysvar_node_st *) entry)->m_svar); +} + + +/** + Prepare/reset the m_registered_sysvars hash for next statement. +*/ + +void Session_sysvars_tracker::reset() +{ + sysvar_node_st *node; + int idx= 0; + + while ((node= (sysvar_node_st *) (*orig_list)[idx])) + { + node->m_changed= false; + ++ idx; + } + m_changed= false; +} + +static Session_sysvars_tracker* sysvar_tracker(THD *thd) +{ + return (Session_sysvars_tracker*) + thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER); +} + +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); +} +bool sysvartrack_reprint_value(THD *thd, char *str, size_t len) +{ + LEX_STRING tmp= {str, len}; + return Session_sysvars_tracker::server_init_process(thd, + system_charset_info, + tmp); +} +bool sysvartrack_update(THD *thd) +{ + return sysvar_tracker(thd)->update(thd); +} +size_t sysvartrack_value_len(THD *thd) +{ + return sysvar_tracker(thd)->get_buffer_length(); +} +bool sysvartrack_value_construct(THD *thd, char *val, size_t len) +{ + return sysvar_tracker(thd)->construct_var_list(val, len); +} + /////////////////////////////////////////////////////////////////////////////// /** @@ -282,18 +994,11 @@ bool Session_state_change_tracker::is_state_changed(THD *) 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; + for (int i= 0; i <= SESSION_TRACKER_END; i ++) + m_trackers[i]= NULL; } + /** @brief Enables the tracker objects. @@ -301,13 +1006,57 @@ Session_tracker::Session_tracker() @return void */ + void Session_tracker::enable(THD *thd) { + /* + Originally and correctly this allocation was in the constructor and + deallocation in the destructor, but in this case memory counting + system works incorrectly (for example in INSERT DELAYED thread) + */ + deinit(); + m_trackers[SESSION_SYSVARS_TRACKER]= + new (std::nothrow) Session_sysvars_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; + for (int i= 0; i <= SESSION_TRACKER_END; i ++) m_trackers[i]->enable(thd); } +/** + Method called during the server startup to verify the contents + of @@session_track_system_variables. + + @retval false Success + @retval true Failure +*/ + +bool Session_tracker::server_boot_verify(const 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; + return result; +} + + /** @brief Store all change information in the specified buffer. diff --git a/sql/session_tracker.h b/sql/session_tracker.h index ec24d5a7a00..7025c34967d 100644 --- a/sql/session_tracker.h +++ b/sql/session_tracker.h @@ -104,6 +104,12 @@ public: virtual void mark_as_changed(THD *thd, LEX_CSTRING *name)= 0; }; +bool sysvartrack_validate_value(THD *thd, const char *str, size_t len); +bool sysvartrack_reprint_value(THD *thd, char *str, size_t len); +bool sysvartrack_update(THD *thd); +size_t sysvartrack_value_len(THD *thd); +bool sysvartrack_value_construct(THD *thd, char *val, size_t len); + /** Session_tracker @@ -134,10 +140,22 @@ public: Session_tracker(); ~Session_tracker() { - for (int i= 0; i <= SESSION_TRACKER_END; i ++) - delete m_trackers[i]; + deinit(); } + + /* trick to make happy memory accounting system */ + void deinit() + { + for (int i= 0; i <= SESSION_TRACKER_END; i ++) + { + if (m_trackers[i]) + delete m_trackers[i]; + m_trackers[i]= NULL; + } + } + void enable(THD *thd); + bool server_boot_verify(const CHARSET_INFO *char_set); /** Returns the pointer to the tracker object for the specified tracker. */ inline State_tracker *get_tracker(enum_session_tracker tracker) const diff --git a/sql/set_var.cc b/sql/set_var.cc index 68d57abcdf6..84ed7810650 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -115,6 +115,9 @@ void sys_var_end() DBUG_VOID_RETURN; } + +static bool static_test_load= TRUE; + /** sys_var constructor @@ -184,6 +187,8 @@ sys_var::sys_var(sys_var_chain *chain, const char *name_arg, else chain->first= this; chain->last= this; + + test_load= &static_test_load; } bool sys_var::update(THD *thd, set_var *var) @@ -215,13 +220,14 @@ bool sys_var::update(THD *thd, set_var *var) */ if ((var->type == OPT_SESSION) && (!ret)) { + thd->session_tracker.mark_as_changed(thd, SESSION_SYSVARS_TRACKER, + (LEX_CSTRING*)var->var); /* 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; @@ -995,7 +1001,30 @@ int set_var_collation_client::update(THD *thd) thd->update_charset(character_set_client, collation_connection, character_set_results); + /* Mark client collation variables as changed */ + if (thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->is_enabled()) + { + sys_var *svar; + mysql_mutex_lock(&LOCK_plugin); + if ((svar= find_sys_var_ex(thd, "character_set_client", + sizeof("character_set_client") - 1, + false, true))) + thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)-> + mark_as_changed(thd, (LEX_CSTRING*)svar); + if ((svar= find_sys_var_ex(thd, "character_set_results", + sizeof("character_set_results") - 1, + false, true))) + thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)-> + mark_as_changed(thd, (LEX_CSTRING*)svar); + if ((svar= find_sys_var_ex(thd, "character_set_connection", + sizeof("character_set_connection") - 1, + false, true))) + thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)-> + mark_as_changed(thd, (LEX_CSTRING*)svar); + mysql_mutex_unlock(&LOCK_plugin); + } 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 6a650f2ec8a..16111ad7111 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -48,6 +48,9 @@ struct sys_var_chain int mysql_add_sys_var_chain(sys_var *chain); int mysql_del_sys_var_chain(sys_var *chain); + +extern const LEX_CSTRING SESSION_TRACK_SYSTEM_VARIABLES_NAME; + /** A class representing one system variable - that is something that can be accessed as @@global.variable_name or @@session.variable_name, @@ -60,6 +63,7 @@ class sys_var: protected Value_source // for double_from_string_with_check public: sys_var *next; LEX_CSTRING name; + bool *test_load; enum flag_enum { GLOBAL, SESSION, ONLY_SESSION, SCOPE_MASK=1023, READONLY=1024, ALLOCATED=2048, PARSE_EARLY=4096, NO_SET_STATEMENT=8192, AUTO_SET=16384}; @@ -240,6 +244,9 @@ protected: uchar *global_var_ptr() { return ((uchar*)&global_system_variables) + offset; } + + friend class Session_sysvars_tracker; + friend class Session_tracker; }; #include "sql_plugin.h" /* SHOW_HA_ROWS, SHOW_MY_BOOL */ diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 3bb1b3a6197..4e301a9df02 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -4064,21 +4064,21 @@ ER_LOCK_OR_ACTIVE_TRANSACTION swe "Kan inte utföra kommandot emedan du har en låst tabell eller an aktiv transaktion" ukr "Не можу виконати подану команду тому, що таблиця заблокована або виконується транзакція" ER_UNKNOWN_SYSTEM_VARIABLE - cze "Neznámá systémová proměnná '%-.64s'" - dan "Ukendt systemvariabel '%-.64s'" - nla "Onbekende systeem variabele '%-.64s'" - eng "Unknown system variable '%-.64s'" - est "Tundmatu süsteemne muutuja '%-.64s'" - fre "Variable système '%-.64s' inconnue" - ger "Unbekannte Systemvariable '%-.64s'" - ita "Variabile di sistema '%-.64s' sconosciuta" - jpn "'%-.64s' は不明なシステム変数です。" - por "Variável de sistema '%-.64s' desconhecida" - rus "Неизвестная системная переменная '%-.64s'" - serbian "Nepoznata sistemska promenljiva '%-.64s'" - spa "Desconocida variable de sistema '%-.64s'" - swe "Okänd systemvariabel: '%-.64s'" - ukr "Невідома системна змінна '%-.64s'" + cze "Neznámá systémová proměnná '%-.*s'" + dan "Ukendt systemvariabel '%-.*s'" + nla "Onbekende systeem variabele '%-.*s'" + eng "Unknown system variable '%-.*s'" + est "Tundmatu süsteemne muutuja '%-.*s'" + fre "Variable système '%-.*s' inconnue" + ger "Unbekannte Systemvariable '%-.*s'" + ita "Variabile di sistema '%-.*s' sconosciuta" + jpn "'%-.*s' は不明なシステム変数です。" + por "Variável de sistema '%-.*s' desconhecida" + rus "Неизвестная системная переменная '%-.*s'" + serbian "Nepoznata sistemska promenljiva '%-.*s'" + spa "Desconocida variable de sistema '%-.*s'" + swe "Okänd systemvariabel: '%-.*s'" + ukr "Невідома системна змінна '%-.*s'" ER_CRASHED_ON_USAGE cze "Tabulka '%-.192s' je označena jako porušená a měla by být opravena" dan "Tabellen '%-.192s' er markeret med fejl og bør repareres" diff --git a/sql/sql_class.cc b/sql/sql_class.cc index e91c80d3f36..a99e375cfbd 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1766,6 +1766,10 @@ THD::~THD() lf_hash_put_pins(xid_hash_pins); /* Ensure everything is freed */ status_var.local_memory_used-= sizeof(THD); + + /* trick to make happy memory accounting system */ + session_tracker.deinit(); + if (status_var.local_memory_used != 0) { DBUG_PRINT("error", ("memory_used: %lld", status_var.local_memory_used)); diff --git a/sql/sql_class.h b/sql/sql_class.h index 7a663bf7653..a6af33f7c5a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -691,6 +691,8 @@ typedef struct system_variables my_bool session_track_schema; my_bool session_track_state_change; + + char *session_track_system_variables; } SV; /** diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 98bc5d606af..db6a4b9b15b 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -269,6 +269,7 @@ struct st_bookmark uint name_len; int offset; uint version; + bool loaded; char key[1]; }; @@ -322,6 +323,8 @@ static void unlock_variables(THD *thd, struct system_variables *vars); static void cleanup_variables(struct system_variables *vars); static void plugin_vars_free_values(sys_var *vars); static void restore_ptr_backup(uint n, st_ptr_backup *backup); +#define my_intern_plugin_lock(A,B) intern_plugin_lock(A,B) +#define my_intern_plugin_lock_ci(A,B) intern_plugin_lock(A,B) static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin); static void intern_plugin_unlock(LEX *lex, plugin_ref plugin); static void reap_plugins(void); @@ -1175,6 +1178,13 @@ err: DBUG_RETURN(errs > 0 || oks + dupes == 0); } +static void plugin_variables_deinit(struct st_plugin_int *plugin) +{ + + for (sys_var *var= plugin->system_vars; var; var= var->next) + (*var->test_load)= FALSE; + mysql_del_sys_var_chain(plugin->system_vars); +} static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check) { @@ -1226,8 +1236,7 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check) if (ref_check && plugin->ref_count) sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.", plugin->name.str, plugin->ref_count); - - mysql_del_sys_var_chain(plugin->system_vars); + plugin_variables_deinit(plugin); } static void plugin_del(struct st_plugin_int *plugin) @@ -1447,7 +1456,7 @@ static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin, err: if (ret) - mysql_del_sys_var_chain(plugin->system_vars); + plugin_variables_deinit(plugin); mysql_mutex_lock(&LOCK_plugin); plugin->state= state; @@ -2780,22 +2789,24 @@ static void update_func_double(THD *thd, struct st_mysql_sys_var *var, System Variables support ****************************************************************************/ - -sys_var *find_sys_var(THD *thd, const char *str, size_t length) +sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length, + bool throw_error, bool locked) { sys_var *var; sys_var_pluginvar *pi= NULL; plugin_ref plugin; - DBUG_ENTER("find_sys_var"); + DBUG_ENTER("find_sys_var_ex"); + DBUG_PRINT("enter", ("var '%.*s'", (int)length, str)); - mysql_mutex_lock(&LOCK_plugin); + if (!locked) + mysql_mutex_lock(&LOCK_plugin); mysql_rwlock_rdlock(&LOCK_system_variables_hash); if ((var= intern_find_sys_var(str, length)) && (pi= var->cast_pluginvar())) { mysql_rwlock_unlock(&LOCK_system_variables_hash); LEX *lex= thd ? thd->lex : 0; - if (!(plugin= intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin)))) + if (!(plugin= my_intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin)))) var= NULL; /* failed to lock it, it must be uninstalling */ else if (!(plugin_state(plugin) & PLUGIN_IS_READY)) @@ -2807,14 +2818,20 @@ sys_var *find_sys_var(THD *thd, const char *str, size_t length) } else mysql_rwlock_unlock(&LOCK_system_variables_hash); - mysql_mutex_unlock(&LOCK_plugin); + if (!locked) + mysql_mutex_unlock(&LOCK_plugin); - if (!var) - my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str); + if (!throw_error && !var) + my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (int)length, (char*) str); DBUG_RETURN(var); } +sys_var *find_sys_var(THD *thd, const char *str, size_t length) +{ + return find_sys_var_ex(thd, str, length, false, false); +} + /* called by register_var, construct_options and test_plugin_options. Returns the 'bookmark' for the named variable. @@ -3940,6 +3957,14 @@ my_bool mark_changed(int, const struct my_option *opt, char *) return 0; } +/** + It is always false to mark global plugin variable unloaded just to be + safe because we have no way now to know truth about them. + + TODO: make correct mechanism for global plugin variables +*/ +static bool static_unload= FALSE; + /** Create and register system variables supplied from the plugin and assigns initial values from corresponding command line arguments. @@ -4017,9 +4042,13 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, tmp_backup[tmp->nbackups++].save(&o->name); if ((var= find_bookmark(tmp->name.str, o->name, o->flags))) + { varname= var->key + 1; + var->loaded= TRUE; + } else { + var= NULL; len= tmp->name.length + strlen(o->name) + 2; varname= (char*) alloc_root(mem_root, len); strxmov(varname, tmp->name.str, "-", o->name, NullS); @@ -4027,6 +4056,9 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp, convert_dash_to_underscore(varname, len-1); } v= new (mem_root) sys_var_pluginvar(&chain, varname, tmp, o); + v->test_load= (var ? &var->loaded : &static_unload); + DBUG_ASSERT(static_unload == FALSE); + if (!(o->flags & PLUGIN_VAR_NOCMDOPT)) { // update app_type, used for I_S.SYSTEM_VARIABLES diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 96f8411f8ed..47c3af83bdf 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -192,4 +192,6 @@ extern bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, extern bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl, plugin_foreach_func *func, void *arg); +sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length, + bool throw_error, bool locked); #endif diff --git a/sql/sql_show.cc b/sql/sql_show.cc index a446e05d427..25af6fe07cc 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3212,6 +3212,132 @@ void remove_status_vars(SHOW_VAR *list) } +/** + @brief Returns the value of a system or a status variable. + + @param thd [in] The handle of the current THD. + @param variable [in] Details of the variable. + @param value_type [in] Variable type. + @param show_type [in] Variable show type. + @param charset [out] Character set of the value. + @param buff [in,out] Buffer to store the value. + (Needs to have enough memory + to hold the value of variable.) + @param length [out] Length of the value. + + @return Pointer to the value buffer. +*/ + +const char* get_one_variable(THD *thd, + const SHOW_VAR *variable, + enum_var_type value_type, SHOW_TYPE show_type, + system_status_var *status_var, + const CHARSET_INFO **charset, char *buff, + size_t *length) +{ + void *value= variable->value; + const char *pos= buff; + const char *end= buff; + + + if (show_type == SHOW_SYS) + { + sys_var *var= (sys_var *) value; + show_type= var->show_type(); + value= var->value_ptr(thd, value_type, &null_lex_str); + *charset= var->charset(thd); + } + + /* + note that value may be == buff. All SHOW_xxx code below + should still work in this case + */ + switch (show_type) { + case SHOW_DOUBLE_STATUS: + value= ((char *) status_var + (intptr) value); + /* fall through */ + case SHOW_DOUBLE: + /* 6 is the default precision for '%f' in sprintf() */ + end= buff + my_fcvt(*(double *) value, 6, buff, NULL); + break; + case SHOW_LONG_STATUS: + value= ((char *) status_var + (intptr) value); + /* fall through */ + case SHOW_ULONG: + case SHOW_LONG_NOFLUSH: // the difference lies in refresh_status() + end= int10_to_str(*(long*) value, buff, 10); + break; + case SHOW_LONGLONG_STATUS: + value= ((char *) status_var + (intptr) value); + /* fall through */ + case SHOW_ULONGLONG: + end= longlong10_to_str(*(longlong*) value, buff, 10); + break; + case SHOW_HA_ROWS: + end= longlong10_to_str((longlong) *(ha_rows*) value, buff, 10); + break; + case SHOW_BOOL: + end= strmov(buff, *(bool*) value ? "ON" : "OFF"); + break; + case SHOW_MY_BOOL: + end= strmov(buff, *(my_bool*) value ? "ON" : "OFF"); + break; + case SHOW_UINT: + end= int10_to_str((long) *(uint*) value, buff, 10); + break; + case SHOW_SINT: + end= int10_to_str((long) *(int*) value, buff, -10); + break; + case SHOW_SLONG: + end= int10_to_str(*(long*) value, buff, -10); + break; + case SHOW_SLONGLONG: + end= longlong10_to_str(*(longlong*) value, buff, -10); + break; + case SHOW_HAVE: + { + SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) value; + pos= show_comp_option_name[(int) tmp]; + end= strend(pos); + break; + } + case SHOW_CHAR: + { + if (!(pos= (char*)value)) + pos= ""; + end= strend(pos); + break; + } + case SHOW_CHAR_PTR: + { + if (!(pos= *(char**) value)) + pos= ""; + + end= strend(pos); + break; + } + case SHOW_LEX_STRING: + { + LEX_STRING *ls=(LEX_STRING*)value; + if (!(pos= ls->str)) + end= pos= ""; + else + end= pos + ls->length; + break; + } + case SHOW_UNDEF: + break; // Return empty string + case SHOW_SYS: // Cannot happen + default: + DBUG_ASSERT(0); + break; + } + + *length= (size_t) (end - pos); + return pos; +} + + static bool show_status_array(THD *thd, const char *wild, SHOW_VAR *variables, enum enum_var_type scope, @@ -3324,109 +3450,21 @@ static bool show_status_array(THD *thd, const char *wild, name_buffer, wild))) && (!cond || cond->val_int())) { - void *value=var->value; - const char *pos, *end; // We assign a lot of const's + const char *pos; // We assign a lot of const's + size_t length; if (show_type == SHOW_SYS) - { - sys_var *var= (sys_var *) value; - show_type= var->show_type(); mysql_mutex_lock(&LOCK_global_system_variables); - value= var->value_ptr(thd, scope, &null_lex_str); - charset= var->charset(thd); - } + pos= get_one_variable(thd, var, scope, show_type, status_var, + &charset, buff, &length); - pos= end= buff; - /* - note that value may be == buff. All SHOW_xxx code below - should still work in this case - */ - switch (show_type) { - case SHOW_DOUBLE_STATUS: - value= ((char *) status_var + (intptr) value); - /* fall through */ - case SHOW_DOUBLE: - /* 6 is the default precision for '%f' in sprintf() */ - end= buff + my_fcvt(*(double *) value, 6, buff, NULL); - break; - case SHOW_LONG_STATUS: - value= ((char *) status_var + (intptr) value); - /* fall through */ - case SHOW_ULONG: - case SHOW_LONG_NOFLUSH: // the difference lies in refresh_status() - end= int10_to_str(*(long*) value, buff, 10); - break; - case SHOW_LONGLONG_STATUS: - value= ((char *) status_var + (intptr) value); - /* fall through */ - case SHOW_ULONGLONG: - end= longlong10_to_str(*(longlong*) value, buff, 10); - break; - case SHOW_HA_ROWS: - end= longlong10_to_str((longlong) *(ha_rows*) value, buff, 10); - break; - case SHOW_BOOL: - end= strmov(buff, *(bool*) value ? "ON" : "OFF"); - break; - case SHOW_MY_BOOL: - end= strmov(buff, *(my_bool*) value ? "ON" : "OFF"); - break; - case SHOW_UINT: - end= int10_to_str((long) *(uint*) value, buff, 10); - break; - case SHOW_SINT: - end= int10_to_str((long) *(int*) value, buff, -10); - break; - case SHOW_SLONG: - end= int10_to_str(*(long*) value, buff, -10); - break; - case SHOW_SLONGLONG: - end= longlong10_to_str(*(longlong*) value, buff, -10); - break; - case SHOW_HAVE: - { - SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) value; - pos= show_comp_option_name[(int) tmp]; - end= strend(pos); - break; - } - case SHOW_CHAR: - { - if (!(pos= (char*)value)) - pos= ""; - end= strend(pos); - break; - } - case SHOW_CHAR_PTR: - { - if (!(pos= *(char**) value)) - pos= ""; - - end= strend(pos); - break; - } - case SHOW_LEX_STRING: - { - LEX_STRING *ls=(LEX_STRING*)value; - if (!(pos= ls->str)) - end= pos= ""; - else - end= pos + ls->length; - break; - } - case SHOW_UNDEF: - break; // Return empty string - case SHOW_SYS: // Cannot happen - default: - DBUG_ASSERT(0); - break; - } - table->field[1]->store(pos, (uint32) (end - pos), charset); + table->field[1]->store(pos, (uint32) length, charset); + thd->count_cuted_fields= CHECK_FIELD_IGNORE; table->field[1]->set_notnull(); - - if (var->type == SHOW_SYS) + if (show_type == SHOW_SYS) mysql_mutex_unlock(&LOCK_global_system_variables); + if (schema_table_store_record(thd, table)) { res= TRUE; diff --git a/sql/sql_show.h b/sql/sql_show.h index dbae2a42b39..e93b855450c 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -131,6 +131,12 @@ bool get_schema_tables_result(JOIN *join, enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table); TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list); +const char* get_one_variable(THD *thd, const SHOW_VAR *variable, + enum_var_type value_type, SHOW_TYPE show_type, + system_status_var *status_var, + const CHARSET_INFO **charset, char *buff, + size_t *length); + /* These functions were under INNODB_COMPATIBILITY_HOOKS */ int get_quote_char_for_identifier(THD *thd, const char *name, uint length); THD *find_thread_by_id(longlong id, bool query_id= false); diff --git a/sql/sql_string.h b/sql/sql_string.h index 10f3c4aee43..feab8070cd2 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -359,7 +359,9 @@ public: if (ALIGN_SIZE(arg_length+1) < Alloced_length) { char *new_ptr; - if (!(new_ptr=(char*) my_realloc(Ptr,arg_length,MYF(0)))) + if (!(new_ptr=(char*) + my_realloc(Ptr, arg_length,MYF((thread_specific ? + MY_THREAD_SPECIFIC : 0))))) { Alloced_length = 0; real_alloc(arg_length); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index ea9f1d14eee..68be0cdbdca 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -5375,6 +5375,16 @@ static Sys_var_ulong Sys_log_tc_size( BLOCK_SIZE(my_getpagesize())); #endif +const LEX_CSTRING SESSION_TRACK_SYSTEM_VARIABLES_NAME= + {STRING_WITH_LEN("session_track_system_variables")}; + +static Sys_var_sesvartrack Sys_track_session_sys_vars( + SESSION_TRACK_SYSTEM_VARIABLES_NAME.str, + "Track changes in registered system variables.", + CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET, + DEFAULT("autocommit,character_set_client,character_set_connection," + "character_set_results,time_zone"), + NO_MUTEX_GUARD); static bool update_session_track_schema(sys_var *self, THD *thd, enum_var_type type) diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index ca6634849a1..dbe84d3efcc 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -438,10 +438,10 @@ public: does not destroy individual members of SV, there's no way to free allocated string variables for every thread. */ -class Sys_var_charptr: public sys_var +class Sys_var_charptr_base: public sys_var { public: - Sys_var_charptr(const char *name_arg, + Sys_var_charptr_base(const char *name_arg, const char *comment, int flag_args, ptrdiff_t off, size_t size, CMD_LINE getopt, enum charset_enum is_os_charset_arg, @@ -463,8 +463,6 @@ public: */ option.var_type|= (flags & ALLOCATED) ? GET_STR_ALLOC : GET_STR; global_var(const char*)= def_val; - SYSVAR_ASSERT(scope() == GLOBAL); - SYSVAR_ASSERT(size == sizeof(char *)); } void cleanup() { @@ -503,31 +501,35 @@ public: } bool do_check(THD *thd, set_var *var) { return do_string_check(thd, var, charset(thd)); } - bool session_update(THD *thd, set_var *var) - { - DBUG_ASSERT(FALSE); - return true; - } - bool global_update(THD *thd, set_var *var) + bool session_update(THD *thd, set_var *var)= 0; + char *global_update_prepare(THD *thd, set_var *var) { char *new_val, *ptr= var->save_result.string_value.str; size_t len=var->save_result.string_value.length; if (ptr) { new_val= (char*)my_memdup(ptr, len+1, MYF(MY_WME)); - if (!new_val) return true; + if (!new_val) return 0; new_val[len]=0; } else new_val= 0; + return new_val; + } + void global_update_finish(char *new_val) + { if (flags & ALLOCATED) my_free(global_var(char*)); flags|= ALLOCATED; global_var(char*)= new_val; - return false; } - void session_save_default(THD *thd, set_var *var) - { DBUG_ASSERT(FALSE); } + bool global_update(THD *thd, set_var *var) + { + char *new_val= global_update_prepare(thd, var); + global_update_finish(new_val); + return (new_val == 0 && var->save_result.string_value.str != 0); + } + void session_save_default(THD *thd, set_var *var)= 0; void global_save_default(THD *thd, set_var *var) { char *ptr= (char*)(intptr)option.def_value; @@ -536,6 +538,105 @@ public: } }; +class Sys_var_charptr: public Sys_var_charptr_base +{ +public: + Sys_var_charptr(const char *name_arg, + const char *comment, int flag_args, ptrdiff_t off, size_t size, + CMD_LINE getopt, + enum charset_enum is_os_charset_arg, + const char *def_val, PolyLock *lock=0, + enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG, + on_check_function on_check_func=0, + on_update_function on_update_func=0, + const char *substitute=0) : + Sys_var_charptr_base(name_arg, comment, flag_args, off, size, getopt, + is_os_charset_arg, def_val, lock, binlog_status_arg, + on_check_func, on_update_func, substitute) + { + SYSVAR_ASSERT(scope() == GLOBAL); + SYSVAR_ASSERT(size == sizeof(char *)); + } + + bool session_update(THD *thd, set_var *var) + { + DBUG_ASSERT(FALSE); + return true; + } + void session_save_default(THD *thd, set_var *var) + { DBUG_ASSERT(FALSE); } +}; + +class Sys_var_sesvartrack: public Sys_var_charptr_base +{ +public: + Sys_var_sesvartrack(const char *name_arg, + const char *comment, + CMD_LINE getopt, + enum charset_enum is_os_charset_arg, + const char *def_val, PolyLock *lock) : + Sys_var_charptr_base(name_arg, comment, + SESSION_VAR(session_track_system_variables), + getopt, is_os_charset_arg, def_val, lock, + VARIABLE_NOT_IN_BINLOG, 0, 0, 0) + {} + bool do_check(THD *thd, set_var *var) + { + if (Sys_var_charptr_base::do_check(thd, var) || + sysvartrack_validate_value(thd, var->save_result.string_value.str, + var->save_result.string_value.length)) + return TRUE; + return FALSE; + } + bool global_update(THD *thd, set_var *var) + { + char *new_val= global_update_prepare(thd, var); + if (new_val) + { + if (sysvartrack_reprint_value(thd, new_val, + var->save_result.string_value.length)) + new_val= 0; + } + global_update_finish(new_val); + return (new_val == 0 && var->save_result.string_value.str != 0); + } + bool session_update(THD *thd, set_var *var) + { + return sysvartrack_update(thd); + } + void session_save_default(THD *thd, set_var *var) + { + var->save_result.string_value.str= global_var(char*); + var->save_result.string_value.length= + strlen(var->save_result.string_value.str); + /* parse and feel list with default values */ + if (thd) + { + bool res= + sysvartrack_validate_value(thd, + var->save_result.string_value.str, + var->save_result.string_value.length); + DBUG_ASSERT(res == 0); + } + } + uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + { + DBUG_ASSERT(thd != NULL); + size_t len= sysvartrack_value_len(thd); + char *res= 0; + char *buf= (char *)my_safe_alloca(len); + if (buf && !sysvartrack_value_construct(thd, buf, len)) + { + size_t len= strlen(buf) + 1; + res= (char*) thd->alloc(len + sizeof(char *)); + if (res) + memcpy((*((char**) res)= res + sizeof(char *)), buf, len); + my_safe_afree(buf, len); + } + return (uchar *)res; + } +}; + class Sys_var_proxy_user: public sys_var { From 0ee3e64c55664332e8e92eda55b43692159fe4fe Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Mon, 30 May 2016 21:22:50 +0200 Subject: [PATCH 5/8] MDEV-8931: (server part of) session state tracking Transaction tracker --- include/mysql.h.pp | 3 +- include/mysql_com.h | 10 +- libmysqld/lib_sql.cc | 4 +- mysql-test/r/mysqld--help.result | 14 +- ...ession_track_system_variables_basic.result | 2 + .../sys_vars/r/sysvars_server_embedded.result | 28 - .../r/sysvars_server_notembedded.result | 18 +- .../session_track_system_variables_basic.test | 4 - sql/lock.cc | 37 + sql/mysqld.cc | 6 +- sql/mysqld.h | 1 - sql/protocol.cc | 46 +- sql/session_tracker.cc | 775 +++++++++++++++--- sql/session_tracker.h | 157 +++- sql/set_var.cc | 11 +- sql/set_var.h | 2 - sql/share/errmsg-utf8.txt | 4 - sql/sp_head.cc | 5 + sql/sql_base.cc | 12 + sql/sql_cache.cc | 29 + sql/sql_class.cc | 4 + sql/sql_class.h | 7 +- sql/sql_db.cc | 6 +- sql/sql_parse.cc | 24 +- sql/sql_plugin.cc | 4 +- sql/sql_prepare.cc | 6 +- sql/sql_string.cc | 6 +- sql/sql_table.cc | 3 +- sql/sys_vars.cc | 58 +- sql/sys_vars.ic | 57 +- sql/transaction.cc | 94 ++- sql/transaction.h | 6 + 32 files changed, 1173 insertions(+), 270 deletions(-) diff --git a/include/mysql.h.pp b/include/mysql.h.pp index 57ce4f78d99..0ef3403626c 100644 --- a/include/mysql.h.pp +++ b/include/mysql.h.pp @@ -94,7 +94,8 @@ enum enum_session_state_type SESSION_TRACK_STATE_CHANGE, SESSION_TRACK_GTIDS, SESSION_TRACK_TRANSACTION_CHARACTERISTICS, - SESSION_TRACK_TRANSACTION_STATE + SESSION_TRACK_TRANSACTION_STATE, + SESSION_TRACK_END }; my_bool my_net_init(NET *net, Vio* vio, void *thd, unsigned int my_flags); void my_net_local_init(NET *net); diff --git a/include/mysql_com.h b/include/mysql_com.h index 7433411f29a..16af9849759 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -550,9 +550,6 @@ enum enum_mysql_set_option /* Type of state change information that the server can include in the Ok packet. - Note : 1) session_state_type shouldn't go past 255 (i.e. 1-byte boundary). - 2) Modify the definition of SESSION_TRACK_END when a new member is - added. */ enum enum_session_state_type { @@ -561,15 +558,14 @@ enum enum_session_state_type SESSION_TRACK_STATE_CHANGE, /* track session state changes */ SESSION_TRACK_GTIDS, SESSION_TRACK_TRANSACTION_CHARACTERISTICS, /* Transaction chistics */ - SESSION_TRACK_TRANSACTION_STATE /* Transaction state */ + SESSION_TRACK_TRANSACTION_STATE, /* Transaction state */ + SESSION_TRACK_END /* must be last */ }; #define SESSION_TRACK_BEGIN SESSION_TRACK_SYSTEM_VARIABLES -#define SESSION_TRACK_END SESSION_TRACK_TRANSACTION_STATE - #define IS_SESSION_STATE_TYPE(T) \ - (((int)(T) >= SESSION_TRACK_BEGIN) && ((T) <= SESSION_TRACK_END)) + (((int)(T) >= SESSION_TRACK_BEGIN) && ((T) < SESSION_TRACK_END)) #define net_new_transaction(net) ((net)->pkt_nr=0) diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 8c01fc8b9b6..aeee99ebe4d 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -1172,8 +1172,8 @@ bool net_send_ok(THD *thd, uint server_status, uint statement_warn_count, ulonglong affected_rows, ulonglong id, const char *message, - bool unused1 __attribute__((unused)), - bool unused2 __attribute__((unused))) + bool unused1, + bool unused2) { DBUG_ENTER("emb_net_send_ok"); MYSQL_DATA *data; diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index 7fc9fedd456..176e8186798 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -904,12 +904,21 @@ The following options may be given as the first argument: --server-id=# Uniquely identifies the server instance in the community of replication partners --session-track-schema - Track changes to the 'default schema'. + Track changes to the default schema. (Defaults to on; use --skip-session-track-schema to disable.) --session-track-state-change - Track changes to the 'session state'. + Track changes to the session state. --session-track-system-variables=name Track changes in registered system variables. + --session-track-transaction-info=name + Track changes to the transaction attributes. OFF to + disable; STATE to track just transaction state (Is there + an active transaction? Does it have any data? etc.); + CHARACTERISTICS to track transaction state and report all + statements needed to start a transaction withthe same + characteristics (isolation level, read only/read + write,snapshot - but not any work done / data modified + within the transaction). --show-slave-auth-info Show user and password in SHOW SLAVE HOSTS on this master. @@ -1395,6 +1404,7 @@ server-id 1 session-track-schema TRUE session-track-state-change FALSE session-track-system-variables autocommit,character_set_client,character_set_connection,character_set_results,time_zone +session-track-transaction-info OFF show-slave-auth-info FALSE silent-startup FALSE skip-grant-tables TRUE diff --git a/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result b/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result index e451a22d322..78ca8ca4ad1 100644 --- a/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result +++ b/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result @@ -17,12 +17,14 @@ VARIABLE_NAME VARIABLE_VALUE SESSION_TRACK_SCHEMA ON SESSION_TRACK_STATE_CHANGE OFF SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SESSION_TRACK_TRANSACTION_INFO OFF # via INFORMATION_SCHEMA.SESSION_VARIABLES SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; VARIABLE_NAME VARIABLE_VALUE SESSION_TRACK_SCHEMA ON SESSION_TRACK_STATE_CHANGE OFF SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SESSION_TRACK_TRANSACTION_INFO OFF SET @global_saved_tmp = @@global.session_track_system_variables; # Altering global variable's value diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index 13733c38a94..eecebce80ad 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -3327,34 +3327,6 @@ NUMERIC_BLOCK_SIZE 1 ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED -VARIABLE_NAME SESSION_TRACK_SCHEMA -SESSION_VALUE ON -GLOBAL_VALUE ON -GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE ON -VARIABLE_SCOPE SESSION -VARIABLE_TYPE BOOLEAN -VARIABLE_COMMENT Track changes to the 'default schema'. -NUMERIC_MIN_VALUE NULL -NUMERIC_MAX_VALUE NULL -NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST OFF,ON -READ_ONLY NO -COMMAND_LINE_ARGUMENT OPTIONAL -VARIABLE_NAME SESSION_TRACK_STATE_CHANGE -SESSION_VALUE OFF -GLOBAL_VALUE OFF -GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE OFF -VARIABLE_SCOPE SESSION -VARIABLE_TYPE BOOLEAN -VARIABLE_COMMENT Track changes to the 'session state'. -NUMERIC_MIN_VALUE NULL -NUMERIC_MAX_VALUE NULL -NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST OFF,ON -READ_ONLY NO -COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME SKIP_EXTERNAL_LOCKING SESSION_VALUE NULL GLOBAL_VALUE ON diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index c0d07280253..3a73ca07402 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -3796,7 +3796,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE ON VARIABLE_SCOPE SESSION VARIABLE_TYPE BOOLEAN -VARIABLE_COMMENT Track changes to the 'default schema'. +VARIABLE_COMMENT Track changes to the default schema. NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL @@ -3810,7 +3810,7 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME DEFAULT_VALUE OFF VARIABLE_SCOPE SESSION VARIABLE_TYPE BOOLEAN -VARIABLE_COMMENT Track changes to the 'session state'. +VARIABLE_COMMENT Track changes to the session state. NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL @@ -3831,6 +3831,20 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME SESSION_TRACK_TRANSACTION_INFO +SESSION_VALUE OFF +GLOBAL_VALUE OFF +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE OFF +VARIABLE_SCOPE SESSION +VARIABLE_TYPE ENUM +VARIABLE_COMMENT Track changes to the transaction attributes. OFF to disable; STATE to track just transaction state (Is there an active transaction? Does it have any data? etc.); CHARACTERISTICS to track transaction state and report all statements needed to start a transaction withthe same characteristics (isolation level, read only/read write,snapshot - but not any work done / data modified within the transaction). +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST OFF,STATE,CHARACTERISTICS +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME SKIP_EXTERNAL_LOCKING SESSION_VALUE NULL GLOBAL_VALUE ON diff --git a/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test b/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test index bbb32bb67a3..90e6052947c 100644 --- a/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test +++ b/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test @@ -12,14 +12,10 @@ SELECT @@session.session_track_system_variables; --echo --echo # via INFORMATION_SCHEMA.GLOBAL_VARIABLES ---disable_warnings SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; ---enable_warnings --echo # via INFORMATION_SCHEMA.SESSION_VARIABLES ---disable_warnings SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; ---enable_warnings # Save the global value to be used to restore the original value. SET @global_saved_tmp = @@global.session_track_system_variables; diff --git a/sql/lock.cc b/sql/lock.cc index 2e44786d6fe..07286324fc5 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -90,6 +90,7 @@ extern HASH open_cache; static int lock_external(THD *thd, TABLE **table,uint count); static int unlock_external(THD *thd, TABLE **table,uint count); + /* Map the return value of thr_lock to an error from errmsg.txt */ static int thr_lock_errno_to_mysql[]= { 0, ER_LOCK_ABORTED, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK }; @@ -243,6 +244,39 @@ void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock) } +/** + Scan array of tables for access types; update transaction tracker + accordingly. + + @param thd The current thread. + @param tables An array of pointers to the tables to lock. + @param count The number of tables to lock. +*/ + +#ifndef EMBEDDED_LIBRARY +static void track_table_access(THD *thd, TABLE **tables, size_t count) +{ + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + { + Transaction_state_tracker *tst= (Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER); + + while (count--) + { + TABLE *t= tables[count]; + + if (t) + tst->add_trx_state(thd, t->reginfo.lock_type, + t->file->has_transactions()); + } + } +} +#else +#define track_table_access(A,B,C) +#endif //EMBEDDED_LIBRARY + + + /** Lock tables. @@ -280,6 +314,9 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags) my_free(sql_lock); sql_lock= 0; } + + track_table_access(thd, tables, count); + DBUG_RETURN(sql_lock); } diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b59c6c7048f..28e91e208e7 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5285,16 +5285,16 @@ static int init_server_components() } plugins_are_initialized= TRUE; /* Don't separate from init function */ +#ifndef EMBEDDED_LIBRARY { - Session_tracker session_track_system_variables_check; - if (session_track_system_variables_check. - server_boot_verify(system_charset_info)) + if (Session_tracker::server_boot_verify(system_charset_info)) { sql_print_error("The variable session_track_system_variables has " "invalid values."); unireg_abort(1); } } +#endif //EMBEDDED_LIBRARY /* we do want to exit if there are any other unknown options */ if (remaining_argc > 1) diff --git a/sql/mysqld.h b/sql/mysqld.h index 68eab815564..846a01a9427 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -135,7 +135,6 @@ extern my_bool lower_case_file_system; extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs; extern my_bool opt_secure_auth; extern const char *current_dbug_option; -extern const char *current_session_track_system_variables; extern char* opt_secure_file_priv; extern char* opt_secure_backup_file_priv; extern size_t opt_secure_backup_file_priv_len; diff --git a/sql/protocol.cc b/sql/protocol.cc index 77dedfbc7d2..9ad9269f3b5 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -198,7 +198,7 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err, @param affected_rows Number of rows changed by statement @param id Auto_increment id for first row (if used) @param message Message to send to the client (Used by mysql_status) - @param is_eof this called inted of old EOF packet + @param is_eof this called instead of old EOF packet @return @retval FALSE The message was successfully sent @@ -217,10 +217,6 @@ net_send_ok(THD *thd, NET *net= &thd->net; StringBuffer store; - /* - To be used to manage the data storage in case session state change - information is present. - */ bool state_changed= false; bool error= FALSE; @@ -269,39 +265,25 @@ net_send_ok(THD *thd, } thd->get_stmt_da()->set_overwrite_status(true); - if ((thd->client_capabilities & CLIENT_SESSION_TRACK)) - { - if (server_status & SERVER_SESSION_STATE_CHANGED) - state_changed= true; - /* the info field */ - if (state_changed || (message && message[0])) - { - store.q_net_store_data((uchar*) message, message ? strlen(message) : 0); - } + state_changed= + (thd->client_capabilities & CLIENT_SESSION_TRACK) && + (server_status & SERVER_SESSION_STATE_CHANGED); - /* 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]) + if (state_changed || (message && message[0])) { - /* the info field, if there is a message to store */ - DBUG_ASSERT(strlen(message) <= MYSQL_ERRMSG_SIZE); - store.q_net_store_data((uchar*) message, strlen(message)); + DBUG_ASSERT(safe_strlen(message) <= MYSQL_ERRMSG_SIZE); + store.q_net_store_data((uchar*) safe_str(message), safe_strlen(message)); } - if (store.length() > MAX_PACKET_LENGTH) + if (unlikely(state_changed)) { - net->error= 1; - net->last_errno= ER_NET_OK_PACKET_TOO_LARGE; - my_error(ER_NET_OK_PACKET_TOO_LARGE, MYF(0)); - DBUG_PRINT("info", ("OK packet too large")); - DBUG_RETURN(1); + store.set_charset(thd->variables.collation_database); + + thd->session_tracker.store(thd, &store); } + + DBUG_ASSERT(store.length() <= MAX_PACKET_LENGTH); + error= my_net_write(net, (const unsigned char*)store.ptr(), store.length()); if (!error && (!skip_flush || is_eof)) error= net_flush(net); diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc index cfbb1704318..bd641ab8d03 100644 --- a/sql/session_tracker.cc +++ b/sql/session_tracker.cc @@ -15,6 +15,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifndef EMBEDDED_LIBRARY #include "sql_plugin.h" #include "session_tracker.h" @@ -26,14 +27,20 @@ #include "sql_plugin.h" #include "set_var.h" +void State_tracker::mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name) +{ + m_changed= true; + thd->lex->safe_to_cache_query= 0; + thd->server_status|= SERVER_SESSION_STATE_CHANGED; +} + + class Not_implemented_tracker : public State_tracker { public: bool enable(THD *thd) { return false; } - bool check(THD *, set_var *) - { return false; } - bool update(THD *) + bool update(THD *, set_var *) { return false; } bool store(THD *, String *) { return false; } @@ -42,7 +49,6 @@ public: }; -static my_bool name_array_filler(void *ptr, void *data_ptr); /** Session_sysvars_tracker @@ -123,7 +129,7 @@ private: } } - uchar* search(sysvar_node_st *node, const sys_var *svar) + uchar* insert_or_search(sysvar_node_st *node, const sys_var *svar) { uchar *res; res= search(svar); @@ -146,7 +152,7 @@ private: void reset(); void copy(vars_list* from, THD *thd); bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error, - const CHARSET_INFO *char_set, bool session_created); + CHARSET_INFO *char_set, bool session_created); bool construct_var_list(char *buf, size_t buf_len); }; /** @@ -184,15 +190,13 @@ public: for session_track_system_variables during the server startup. */ - static bool server_init_check(THD *thd, const CHARSET_INFO *char_set, + static bool server_init_check(THD *thd, CHARSET_INFO *char_set, LEX_STRING var_list) { - vars_list dummy; - bool result; - result= dummy.parse_var_list(thd, var_list, false, char_set, false); - return result; + return check_var_list(thd, var_list, false, char_set, false); } - static bool server_init_process(THD *thd, const CHARSET_INFO *char_set, + + static bool server_init_process(THD *thd, CHARSET_INFO *char_set, LEX_STRING var_list) { vars_list dummy; @@ -205,16 +209,17 @@ public: void reset(); bool enable(THD *thd); - bool check(THD *thd, set_var *var); - bool check_str(THD *thd, LEX_STRING val); - bool update(THD *thd); + bool check_str(THD *thd, LEX_STRING *val); + bool update(THD *thd, set_var *var); bool store(THD *thd, String *buf); void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name); /* callback */ static uchar *sysvars_get_key(const char *entry, size_t *length, my_bool not_used __attribute__((unused))); - friend my_bool name_array_filler(void *ptr, void *data_ptr); + static my_bool name_array_filler(void *ptr, void *data_ptr); + static bool check_var_list(THD *thd, LEX_STRING var_list, bool throw_error, + CHARSET_INFO *char_set, bool session_created); }; @@ -240,12 +245,9 @@ public: } bool enable(THD *thd) - { return update(thd); } - bool check(THD *thd, set_var *var) - { return false; } - bool update(THD *thd); + { return update(thd, NULL); } + bool update(THD *thd, set_var *var); bool store(THD *thd, String *buf); - void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name); }; /* @@ -271,15 +273,10 @@ private: public: Session_state_change_tracker(); bool enable(THD *thd) - { return update(thd); }; - bool check(THD *thd, set_var *var) - { return false; } - bool update(THD *thd); + { return update(thd, NULL); }; + bool update(THD *thd, set_var *var); bool store(THD *thd, String *buf); - void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name); bool is_state_changed(THD*); - void ensure_enabled(THD *thd) - {} }; @@ -381,7 +378,7 @@ bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node, bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error, - const CHARSET_INFO *char_set, + CHARSET_INFO *char_set, bool session_created) { const char separator= ','; @@ -463,6 +460,80 @@ error: return true; } + +bool Session_sysvars_tracker::check_var_list(THD *thd, + LEX_STRING var_list, + bool throw_error, + CHARSET_INFO *char_set, + bool session_created) +{ + const char separator= ','; + char *token, *lasts= NULL; + size_t rest= var_list.length; + + if (!var_list.str || var_list.length == 0 || + !strcmp(var_list.str,(const char *)"*")) + { + return false; + } + + token= var_list.str; + + /* + If Lock to the plugin mutex is not acquired here itself, it results + in having to acquire it multiple times in find_sys_var_ex for each + token value. Hence the mutex is handled here to avoid a performance + overhead. + */ + if (!thd || session_created) + mysql_mutex_lock(&LOCK_plugin); + for (;;) + { + sys_var *svar; + LEX_STRING var; + + lasts= (char *) memchr(token, separator, rest); + + var.str= token; + if (lasts) + { + var.length= (lasts - token); + rest-= var.length + 1; + } + else + var.length= rest; + + /* Remove leading/trailing whitespace. */ + trim_whitespace(char_set, &var); + + if (!(svar= find_sys_var_ex(thd, var.str, var.length, throw_error, true))) + { + if (throw_error && session_created && thd) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_VALUE_FOR_VAR, + "%.*s is not a valid system variable and will" + "be ignored.", (int)var.length, token); + } + else + { + if (!thd || session_created) + mysql_mutex_unlock(&LOCK_plugin); + return true; + } + } + + if (lasts) + token= lasts + 1; + else + break; + } + if (!thd || session_created) + mysql_mutex_unlock(&LOCK_plugin); + + return false; +} + struct name_array_filler_data { LEX_CSTRING **names; @@ -471,7 +542,8 @@ struct name_array_filler_data }; /** Collects variable references into array */ -static my_bool name_array_filler(void *ptr, void *data_ptr) +my_bool Session_sysvars_tracker::name_array_filler(void *ptr, + void *data_ptr) { Session_sysvars_tracker::sysvar_node_st *node= (Session_sysvars_tracker::sysvar_node_st *)ptr; @@ -578,18 +650,11 @@ bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf, bool Session_sysvars_tracker::enable(THD *thd) { - sys_var *svar; - mysql_mutex_lock(&LOCK_plugin); - svar= find_sys_var_ex(thd, SESSION_TRACK_SYSTEM_VARIABLES_NAME.str, - SESSION_TRACK_SYSTEM_VARIABLES_NAME.length, - false, true); - DBUG_ASSERT(svar); - - set_var tmp(thd, SHOW_OPT_GLOBAL, svar, &null_lex_str, NULL); - svar->session_save_default(thd, &tmp); - - if (tool_list->parse_var_list(thd, tmp.save_result.string_value, + LEX_STRING tmp; + tmp.str= global_system_variables.session_track_system_variables; + tmp.length= safe_strlen(tmp.str); + if (tool_list->parse_var_list(thd, tmp, true, thd->charset(), false) == true) { mysql_mutex_unlock(&LOCK_plugin); @@ -617,16 +682,10 @@ bool Session_sysvars_tracker::enable(THD *thd) @retval false Success */ -inline bool Session_sysvars_tracker::check(THD *thd, set_var *var) +inline bool Session_sysvars_tracker::check_str(THD *thd, LEX_STRING *val) { - return check_str(thd, var->save_result.string_value); -} - -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); @@ -881,18 +949,6 @@ bool Current_schema_tracker::store(THD *thd, String *buf) } -/** - 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. @@ -905,6 +961,514 @@ void Current_schema_tracker::reset() } +/////////////////////////////////////////////////////////////////////////////// + + +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. +*/ + +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 +*/ + +enum_tx_state Transaction_state_tracker::calc_trx_state(THD *thd, + thr_lock_type l, + bool has_trx) +{ + 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); + } +} + + /////////////////////////////////////////////////////////////////////////////// Session_state_change_tracker::Session_state_change_tracker() @@ -920,7 +1484,7 @@ Session_state_change_tracker::Session_state_change_tracker() **/ -bool Session_state_change_tracker::update(THD *thd) +bool Session_state_change_tracker::update(THD *thd, set_var *) { m_enabled= thd->variables.session_track_state_change; return false; @@ -938,12 +1502,13 @@ bool Session_state_change_tracker::update(THD *thd) bool Session_state_change_tracker::store(THD *thd, String *buf) { - if (buf->prep_alloc(1 + 1 + 1, EXTRA_ALLOC)) + if (unlikely((1 + 1 + 1 + buf->length() >= MAX_PACKET_LENGTH) || + buf->prep_alloc(1 + 1 + 1, EXTRA_ALLOC))) return true; compile_time_assert(SESSION_TRACK_STATE_CHANGE < 251); /* Session state type (SESSION_TRACK_STATE_CHANGE) */ - buf->q_net_store_length((ulonglong)SESSION_TRACK_STATE_CHANGE); + buf->q_append((char)SESSION_TRACK_STATE_CHANGE); /* Length of the overall entity (1 byte) */ buf->q_append('\1'); @@ -956,17 +1521,6 @@ bool Session_state_change_tracker::store(THD *thd, String *buf) return false; } -/** - Mark the tracker as changed and associated session - attributes accordingly. -*/ - -void Session_state_change_tracker::mark_as_changed(THD *thd, LEX_CSTRING *) -{ - m_changed= true; - thd->lex->safe_to_cache_query= 0; - thd->server_status|= SERVER_SESSION_STATE_CHANGED; -} /** Reset the m_changed flag for next statement. @@ -977,6 +1531,7 @@ void Session_state_change_tracker::reset() m_changed= false; } + /** Find if there is a session state change. */ @@ -994,7 +1549,12 @@ bool Session_state_change_tracker::is_state_changed(THD *) Session_tracker::Session_tracker() { - for (int i= 0; i <= SESSION_TRACKER_END; i ++) + /* track data ID fit into one byte in net coding */ + compile_time_assert(SESSION_TRACK_END < 251); + /* one tracker could serv several tracking data */ + compile_time_assert((uint)SESSION_TRACK_END >= (uint)SESSION_TRACKER_END); + + for (int i= 0; i < SESSION_TRACKER_END; i++) m_trackers[i]= NULL; } @@ -1024,9 +1584,9 @@ void Session_tracker::enable(THD *thd) m_trackers[SESSION_GTIDS_TRACKER]= new (std::nothrow) Not_implemented_tracker; m_trackers[TRANSACTION_INFO_TRACKER]= - new (std::nothrow) Not_implemented_tracker; + new (std::nothrow) Transaction_state_tracker; - for (int i= 0; i <= SESSION_TRACKER_END; i ++) + for (int i= 0; i < SESSION_TRACKER_END; i++) m_trackers[i]->enable(thd); } @@ -1039,20 +1599,14 @@ void Session_tracker::enable(THD *thd) @retval true Failure */ -bool Session_tracker::server_boot_verify(const CHARSET_INFO *char_set) +bool Session_tracker::server_boot_verify(CHARSET_INFO *char_set) { - Session_sysvars_tracker *server_tracker; bool result; - sys_var *svar= find_sys_var_ex(NULL, SESSION_TRACK_SYSTEM_VARIABLES_NAME.str, - SESSION_TRACK_SYSTEM_VARIABLES_NAME.length, - false, true); - DBUG_ASSERT(svar); - set_var tmp(NULL, SHOW_OPT_GLOBAL, svar, &null_lex_str, NULL); - svar->session_save_default(NULL, &tmp); - server_tracker= new (std::nothrow) Session_sysvars_tracker(); - result= server_tracker->server_init_check(NULL, char_set, - tmp.save_result.string_value); - delete server_tracker; + LEX_STRING tmp; + tmp.str= global_system_variables.session_track_system_variables; + tmp.length= safe_strlen(tmp.str); + result= + Session_sysvars_tracker::server_init_check(NULL, char_set, tmp); return result; } @@ -1067,7 +1621,6 @@ bool Session_tracker::server_boot_verify(const CHARSET_INFO *char_set) void Session_tracker::store(THD *thd, String *buf) { - /* Temporary buffer to store all the changes. */ size_t start; /* @@ -1079,7 +1632,7 @@ void Session_tracker::store(THD *thd, String *buf) start= buf->length(); /* Get total length. */ - for (int i= 0; i <= SESSION_TRACKER_END; i ++) + for (int i= 0; i < SESSION_TRACKER_END; i++) { if (m_trackers[i]->is_changed() && m_trackers[i]->store(thd, buf)) @@ -1105,3 +1658,5 @@ void Session_tracker::store(THD *thd, String *buf) net_store_length(data - 1, length); } + +#endif //EMBEDDED_LIBRARY diff --git a/sql/session_tracker.h b/sql/session_tracker.h index 7025c34967d..431726f03ed 100644 --- a/sql/session_tracker.h +++ b/sql/session_tracker.h @@ -20,6 +20,7 @@ #include "m_string.h" #include "thr_lock.h" +#ifndef EMBEDDED_LIBRARY /* forward declarations */ class THD; class set_var; @@ -32,12 +33,10 @@ enum enum_session_tracker CURRENT_SCHEMA_TRACKER, /* Current schema */ SESSION_STATE_CHANGE_TRACKER, SESSION_GTIDS_TRACKER, /* Tracks GTIDs */ - TRANSACTION_INFO_TRACKER /* Transaction state */ + TRANSACTION_INFO_TRACKER, /* Transaction state */ + SESSION_TRACKER_END /* must be the last */ }; -#define SESSION_TRACKER_END TRANSACTION_INFO_TRACKER - - /** State_tracker @@ -54,8 +53,7 @@ enum enum_session_tracker the respective system variable either through SET command or via command line option. As required in system variable handling, this interface also includes two functions to help in the verification of the supplied value - (ON_CHECK) and the updation (ON_UPDATE) of the tracker system variable, - namely - check() and update(). + (ON_UPDATE) of the tracker system variable, namely - update(). */ class State_tracker @@ -91,22 +89,19 @@ public: /** Called in the constructor of THD*/ virtual bool enable(THD *thd)= 0; - /** To be invoked when the tracker's system variable is checked (ON_CHECK). */ - virtual bool check(THD *thd, set_var *var)= 0; - /** To be invoked when the tracker's system variable is updated (ON_UPDATE).*/ - virtual bool update(THD *thd)= 0; + virtual bool update(THD *thd, set_var *var)= 0; /** Store changed data into the given buffer. */ virtual bool store(THD *thd, String *buf)= 0; /** Mark the entity as changed. */ - virtual void mark_as_changed(THD *thd, LEX_CSTRING *name)= 0; + virtual void mark_as_changed(THD *thd, LEX_CSTRING *name); }; bool sysvartrack_validate_value(THD *thd, const char *str, size_t len); bool sysvartrack_reprint_value(THD *thd, char *str, size_t len); -bool sysvartrack_update(THD *thd); +bool sysvartrack_update(THD *thd, set_var *var); size_t sysvartrack_value_len(THD *thd); bool sysvartrack_value_construct(THD *thd, char *val, size_t len); @@ -122,7 +117,7 @@ bool sysvartrack_value_construct(THD *thd, char *val, size_t len); class Session_tracker { private: - State_tracker *m_trackers[SESSION_TRACKER_END + 1]; + State_tracker *m_trackers[SESSION_TRACKER_END]; /* The following two functions are private to disable copying. */ Session_tracker(Session_tracker const &other) @@ -146,7 +141,7 @@ public: /* trick to make happy memory accounting system */ void deinit() { - for (int i= 0; i <= SESSION_TRACKER_END; i ++) + for (int i= 0; i < SESSION_TRACKER_END; i++) { if (m_trackers[i]) delete m_trackers[i]; @@ -155,7 +150,7 @@ public: } void enable(THD *thd); - bool server_boot_verify(const CHARSET_INFO *char_set); + static bool server_boot_verify(CHARSET_INFO *char_set); /** Returns the pointer to the tracker object for the specified tracker. */ inline State_tracker *get_tracker(enum_session_tracker tracker) const @@ -174,4 +169,136 @@ public: void store(THD *thd, String *main_buf); }; + +/* + Transaction_state_tracker +*/ + +/** + Transaction state (no transaction, transaction active, work attached, etc.) +*/ +enum enum_tx_state { + TX_EMPTY = 0, ///< "none of the below" + TX_EXPLICIT = 1, ///< an explicit transaction is active + TX_IMPLICIT = 2, ///< an implicit transaction is active + TX_READ_TRX = 4, ///< transactional reads were done + TX_READ_UNSAFE = 8, ///< non-transaction reads were done + TX_WRITE_TRX = 16, ///< transactional writes were done + TX_WRITE_UNSAFE = 32, ///< non-transactional writes were done + TX_STMT_UNSAFE = 64, ///< "unsafe" (non-deterministic like UUID()) stmts + TX_RESULT_SET = 128, ///< result set was sent + TX_WITH_SNAPSHOT= 256, ///< WITH CONSISTENT SNAPSHOT was used + TX_LOCKED_TABLES= 512 ///< LOCK TABLES is active +}; + + +/** + Transaction access mode +*/ +enum enum_tx_read_flags { + TX_READ_INHERIT = 0, ///< not explicitly set, inherit session.tx_read_only + TX_READ_ONLY = 1, ///< START TRANSACTION READ ONLY, or tx_read_only=1 + TX_READ_WRITE = 2, ///< START TRANSACTION READ WRITE, or tx_read_only=0 +}; + + +/** + Transaction isolation level +*/ +enum enum_tx_isol_level { + TX_ISOL_INHERIT = 0, ///< not explicitly set, inherit session.tx_isolation + TX_ISOL_UNCOMMITTED = 1, + TX_ISOL_COMMITTED = 2, + TX_ISOL_REPEATABLE = 3, + TX_ISOL_SERIALIZABLE= 4 +}; + + +/** + Transaction tracking level +*/ +enum enum_session_track_transaction_info { + TX_TRACK_NONE = 0, ///< do not send tracker items on transaction info + TX_TRACK_STATE = 1, ///< track transaction status + TX_TRACK_CHISTICS = 2 ///< track status and characteristics +}; + + +/** + This is a tracker class that enables & manages the tracking of + current transaction info for a particular connection. +*/ + +class Transaction_state_tracker : public State_tracker +{ +private: + /** Helper function: turn table info into table access flag */ + enum_tx_state calc_trx_state(THD *thd, thr_lock_type l, bool has_trx); +public: + /** Constructor */ + Transaction_state_tracker(); + bool enable(THD *thd) + { return update(thd, NULL); } + bool update(THD *thd, set_var *var); + bool store(THD *thd, String *buf); + + /** Change transaction characteristics */ + void set_read_flags(THD *thd, enum enum_tx_read_flags flags); + void set_isol_level(THD *thd, enum enum_tx_isol_level level); + + /** Change transaction state */ + void clear_trx_state(THD *thd, uint clear); + void add_trx_state(THD *thd, uint add); + void inline add_trx_state(THD *thd, thr_lock_type l, bool has_trx) + { + add_trx_state(thd, calc_trx_state(thd, l, has_trx)); + } + void add_trx_state_from_thd(THD *thd); + void end_trx(THD *thd); + + +private: + enum enum_tx_changed { + TX_CHG_NONE = 0, ///< no changes from previous stmt + TX_CHG_STATE = 1, ///< state has changed from previous stmt + TX_CHG_CHISTICS = 2 ///< characteristics have changed from previous stmt + }; + + /** any trackable changes caused by this statement? */ + uint tx_changed; + + /** transaction state */ + uint tx_curr_state, tx_reported_state; + + /** r/w or r/o set? session default? */ + enum enum_tx_read_flags tx_read_flags; + + /** isolation level */ + enum enum_tx_isol_level tx_isol_level; + + void reset(); + + inline void update_change_flags(THD *thd) + { + tx_changed &= ~TX_CHG_STATE; + tx_changed |= (tx_curr_state != tx_reported_state) ? TX_CHG_STATE : 0; + if (tx_changed != TX_CHG_NONE) + mark_as_changed(thd, NULL); + } +}; + +#define TRANSACT_TRACKER(X) \ + do { if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) \ + {((Transaction_state_tracker *) \ + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER)) \ + ->X; } } while(0) +#define SESSION_TRACKER_CHANGED(A,B,C) \ + thd->session_tracker.mark_as_changed(A,B,C) +#else + +#define TRANSACT_TRACKER(X) do{}while(0) +#define SESSION_TRACKER_CHANGED(A,B,C) do{}while(0) + +#endif //EMBEDDED_LIBRARY + #endif /* SESSION_TRACKER_INCLUDED */ diff --git a/sql/set_var.cc b/sql/set_var.cc index 84ed7810650..26eb5127a0b 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -220,14 +220,13 @@ bool sys_var::update(THD *thd, set_var *var) */ if ((var->type == OPT_SESSION) && (!ret)) { - thd->session_tracker.mark_as_changed(thd, SESSION_SYSVARS_TRACKER, - (LEX_CSTRING*)var->var); + SESSION_TRACKER_CHANGED(thd, SESSION_SYSVARS_TRACKER, + (LEX_CSTRING*)var->var); /* Here MySQL sends variable name to avoid reporting change of the tracker itself, but we decided that it is not needed */ - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, - NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); } return ret; @@ -894,7 +893,7 @@ int set_var_user::update(THD *thd) return -1; } - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); return 0; } @@ -1002,6 +1001,7 @@ int set_var_collation_client::update(THD *thd) character_set_results); /* Mark client collation variables as changed */ +#ifndef EMBEDDED_LIBRARY if (thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->is_enabled()) { sys_var *svar; @@ -1024,6 +1024,7 @@ int set_var_collation_client::update(THD *thd) mysql_mutex_unlock(&LOCK_plugin); } thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL); +#endif //EMBEDDED_LIBRARY thd->protocol_text.init(thd); thd->protocol_binary.init(thd); diff --git a/sql/set_var.h b/sql/set_var.h index 16111ad7111..ba8027edc72 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -49,8 +49,6 @@ int mysql_add_sys_var_chain(sys_var *chain); int mysql_del_sys_var_chain(sys_var *chain); -extern const LEX_CSTRING SESSION_TRACK_SYSTEM_VARIABLES_NAME; - /** A class representing one system variable - that is something that can be accessed as @@global.variable_name or @@session.variable_name, diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 4e301a9df02..361d68ff76a 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7150,10 +7150,6 @@ skip-to-error-number 3000 ER_MYSQL_57_TEST eng "5.7 test" -ER_NET_OK_PACKET_TOO_LARGE 08S01 - eng "OK packet too large" - ukr "Пакет OK надто великий" - # MariaDB extra error numbers starts from 4000 skip-to-error-number 4000 diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 8488e8dfd62..c344a6c6ed8 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2048,6 +2048,8 @@ sp_head::execute_procedure(THD *thd, List *args) break; } } + + TRANSACT_TRACKER(add_trx_state_from_thd(thd)); } /* @@ -3063,6 +3065,9 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, what is needed from the substatement gained */ thd->transaction.stmt.modified_non_trans_table |= parent_modified_non_trans_table; + + TRANSACT_TRACKER(add_trx_state_from_thd(thd)); + /* Unlike for PS we should not call Item's destructors for newly created items after execution of each instruction in stored routine. This is diff --git a/sql/sql_base.cc b/sql/sql_base.cc index d7812db53bd..7757068b265 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2093,6 +2093,9 @@ Locked_tables_list::init_locked_tables(THD *thd) return TRUE; } } + + TRANSACT_TRACKER(add_trx_state(thd, TX_LOCKED_TABLES)); + thd->enter_locked_tables_mode(LTM_LOCK_TABLES); return FALSE; @@ -2133,6 +2136,8 @@ Locked_tables_list::unlock_locked_tables(THD *thd) } thd->leave_locked_tables_mode(); + TRANSACT_TRACKER(clear_trx_state(thd, TX_LOCKED_TABLES)); + DBUG_ASSERT(thd->transaction.stmt.is_empty()); close_thread_tables(thd); @@ -4354,6 +4359,13 @@ static bool check_lock_and_start_stmt(THD *thd, table_list->table->file->print_error(error, MYF(0)); DBUG_RETURN(1); } + + /* + Record in transaction state tracking + */ + TRANSACT_TRACKER(add_trx_state(thd, lock_type, + table_list->table->file->has_transactions())); + DBUG_RETURN(0); } diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 06c5f992939..8ff4684f0ff 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1381,6 +1381,21 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) DBUG_VOID_RETURN; } + /* + Do not store queries while tracking transaction state. + The tracker already flags queries that actually have + transaction tracker items, but this will make behavior + more straight forward. + */ +#ifndef EMBEDDED_LIBRARY + if (thd->variables.session_track_transaction_info != TX_TRACK_NONE) + { + DBUG_PRINT("qcache", ("Do not work with transaction tracking")); + DBUG_VOID_RETURN; + } +#endif //EMBEDDED_LIBRARY + + /* The following assert fails if we haven't called send_result_to_client */ DBUG_ASSERT(thd->base_query.is_alloced() || thd->base_query.ptr() == thd->query()); @@ -1719,6 +1734,20 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) goto err; } + /* + Don't allow serving from Query_cache while tracking transaction + state. This is a safeguard in case an otherwise matching query + was added to the cache before tracking was turned on. + */ +#ifndef EMBEDDED_LIBRARY + if (thd->variables.session_track_transaction_info != TX_TRACK_NONE) + { + DBUG_PRINT("qcache", ("Do not work with transaction tracking")); + goto err; + } +#endif //EMBEDDED_LIBRARY + + thd->query_cache_is_applicable= 1; sql= org_sql; sql_end= sql + query_length; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index a99e375cfbd..54bc4b9959f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1466,7 +1466,9 @@ void THD::init(void) debug_sync_init_thread(this); #endif /* defined(ENABLED_DEBUG_SYNC) */ +#ifndef EMBEDDED_LIBRARY session_tracker.enable(this); +#endif //EMBEDDED_LIBRARY apc_target.init(&LOCK_thd_data); DBUG_VOID_RETURN; @@ -1768,7 +1770,9 @@ THD::~THD() status_var.local_memory_used-= sizeof(THD); /* trick to make happy memory accounting system */ +#ifndef EMBEDDED_LIBRARY session_tracker.deinit(); +#endif //EMBEDDED_LIBRARY if (status_var.local_memory_used != 0) { diff --git a/sql/sql_class.h b/sql/sql_class.h index a6af33f7c5a..1af6d1d87d3 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -689,10 +689,11 @@ typedef struct system_variables my_bool pseudo_slave_mode; + char *session_track_system_variables; + ulong session_track_transaction_info; my_bool session_track_schema; my_bool session_track_state_change; - char *session_track_system_variables; } SV; /** @@ -4059,7 +4060,9 @@ private: LEX_STRING invoker_host; public: +#ifndef EMBEDDED_LIBRARY Session_tracker session_tracker; +#endif //EMBEDDED_LIBRARY /* Flag, mutex and condition for a thread to wait for a signal from another thread. @@ -4293,6 +4296,8 @@ my_eof(THD *thd) { thd->set_row_count_func(-1); thd->get_stmt_da()->set_eof_status(thd); + + TRANSACT_TRACKER(add_trx_state(thd, TX_RESULT_SET)); } #define tmp_disable_binlog(A) \ diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 128281c7686..20538fe1fb4 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1037,7 +1037,7 @@ exit: if (thd->db && cmp_db_names(thd->db, db) && !error) { mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); - thd->session_tracker.mark_as_changed(thd, CURRENT_SCHEMA_TRACKER, NULL); + SESSION_TRACKER_CHANGED(thd, CURRENT_SCHEMA_TRACKER, NULL); } my_dirend(dirp); DBUG_RETURN(error); @@ -1591,8 +1591,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) mysql_change_db_impl(thd, &new_db_file_name, db_access, db_default_cl); done: - thd->session_tracker.mark_as_changed(thd, CURRENT_SCHEMA_TRACKER, NULL); - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL); + SESSION_TRACKER_CHANGED(thd, CURRENT_SCHEMA_TRACKER, NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); DBUG_RETURN(FALSE); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7cb97d156cb..8dc34c2dfe2 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3855,9 +3855,9 @@ mysql_execute_command(THD *thd) /* in case of create temp tables if @@session_track_state_change is ON then send session state notification in OK packet */ if(create_info.options & HA_LEX_CREATE_TMP_TABLE) - thd->session_tracker.mark_as_changed(thd, - SESSION_STATE_CHANGE_TRACKER, - NULL); + { + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); + } my_ok(thd); } } @@ -4619,8 +4619,7 @@ end_with_restore_list: send the boolean tracker in the OK packet */ if(!res && (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) { - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, - NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); } break; } @@ -5433,8 +5432,7 @@ end_with_restore_list: else { /* Reset the isolation level and access mode if no chaining transaction.*/ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); } /* Disconnect the current client connection. */ if (tx_release) @@ -5481,8 +5479,7 @@ end_with_restore_list: else { /* Reset the isolation level and access mode if no chaining transaction.*/ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); } /* Disconnect the current client connection. */ if (tx_release) @@ -5967,8 +5964,7 @@ end_with_restore_list: We've just done a commit, reset transaction isolation level and access mode to the session default. */ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); my_ok(thd); break; } @@ -5986,8 +5982,7 @@ end_with_restore_list: We've just done a rollback, reset transaction isolation level and access mode to the session default. */ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); my_ok(thd); break; } @@ -6205,6 +6200,9 @@ finish: { thd->mdl_context.release_statement_locks(); } + + TRANSACT_TRACKER(add_trx_state_from_thd(thd)); + WSREP_TO_ISOLATION_END; #ifdef WITH_WSREP diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index db6a4b9b15b..b715e33ae62 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -323,8 +323,6 @@ static void unlock_variables(THD *thd, struct system_variables *vars); static void cleanup_variables(struct system_variables *vars); static void plugin_vars_free_values(sys_var *vars); static void restore_ptr_backup(uint n, st_ptr_backup *backup); -#define my_intern_plugin_lock(A,B) intern_plugin_lock(A,B) -#define my_intern_plugin_lock_ci(A,B) intern_plugin_lock(A,B) static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin); static void intern_plugin_unlock(LEX *lex, plugin_ref plugin); static void reap_plugins(void); @@ -2806,7 +2804,7 @@ sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length, { mysql_rwlock_unlock(&LOCK_system_variables_hash); LEX *lex= thd ? thd->lex : 0; - if (!(plugin= my_intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin)))) + if (!(plugin= intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin)))) var= NULL; /* failed to lock it, it must be uninstalling */ else if (!(plugin_state(plugin) & PLUGIN_IS_READY)) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index cc41bd6284e..eab2863588d 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2759,8 +2759,7 @@ void mysql_sql_stmt_prepare(THD *thd) } else { - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, - NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); my_ok(thd, 0L, 0L, "Statement prepared"); } @@ -3212,8 +3211,7 @@ void mysql_sql_stmt_close(THD *thd) else { stmt->deallocate(); - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, - NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); my_ok(thd); } } diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 28e7b899133..a5f266b2d2c 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -1161,13 +1161,15 @@ uint convert_to_printable(char *to, size_t to_len, void String::q_net_store_length(ulonglong length) { + DBUG_ASSERT(Alloced_length >= (str_length + net_length_size(length))); char *pos= (char *) net_store_length((uchar *)(Ptr + str_length), length); str_length= pos - Ptr; } void String::q_net_store_data(const uchar *from, size_t length) { + DBUG_ASSERT(Alloced_length >= (str_length + length + + net_length_size(length))); q_net_store_length(length); - bool res= append((const char *)from, length); - DBUG_ASSERT(!res); + q_append((const char *)from, length); } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index bed116a2930..7784a2b188a 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -9230,8 +9230,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, goto err_new_table_cleanup; } /* in case of alter temp table send the tracker in OK packet */ - thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, - NULL); + SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 68be0cdbdca..462bfe52741 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3363,6 +3363,20 @@ bool Sys_var_tx_read_only::session_update(THD *thd, set_var *var) { // @see Sys_var_tx_isolation::session_update() above for the rules. thd->tx_read_only= var->save_result.ulonglong_value; + +#ifndef EMBEDDED_LIBRARY + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + { + Transaction_state_tracker *tst= (Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER); + + if (var->type == OPT_DEFAULT) + tst->set_read_flags(thd, + thd->tx_read_only ? TX_READ_ONLY : TX_READ_WRITE); + else + tst->set_read_flags(thd, TX_READ_INHERIT); + } +#endif //EMBEDDED_LIBRARY } return false; } @@ -5375,11 +5389,10 @@ static Sys_var_ulong Sys_log_tc_size( BLOCK_SIZE(my_getpagesize())); #endif -const LEX_CSTRING SESSION_TRACK_SYSTEM_VARIABLES_NAME= - {STRING_WITH_LEN("session_track_system_variables")}; +#ifndef EMBEDDED_LIBRARY static Sys_var_sesvartrack Sys_track_session_sys_vars( - SESSION_TRACK_SYSTEM_VARIABLES_NAME.str, + "session_track_system_variables", "Track changes in registered system variables.", CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET, DEFAULT("autocommit,character_set_client,character_set_connection," @@ -5390,30 +5403,61 @@ static bool update_session_track_schema(sys_var *self, THD *thd, enum_var_type type) { DBUG_ENTER("update_session_track_schema"); - DBUG_RETURN(thd->session_tracker.get_tracker(CURRENT_SCHEMA_TRACKER)->update(thd)); + DBUG_RETURN(thd->session_tracker.get_tracker(CURRENT_SCHEMA_TRACKER)-> + update(thd, NULL)); } static Sys_var_mybool Sys_session_track_schema( "session_track_schema", - "Track changes to the 'default schema'.", + "Track changes to the default schema.", SESSION_VAR(session_track_schema), CMD_LINE(OPT_ARG), DEFAULT(TRUE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(update_session_track_schema)); + +static bool update_session_track_tx_info(sys_var *self, THD *thd, + enum_var_type type) +{ + DBUG_ENTER("update_session_track_tx_info"); + DBUG_RETURN(thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER)-> + update(thd, NULL)); +} + +static const char *session_track_transaction_info_names[]= + { "OFF", "STATE", "CHARACTERISTICS", NullS }; + +static Sys_var_enum Sys_session_track_transaction_info( + "session_track_transaction_info", + "Track changes to the transaction attributes. OFF to disable; " + "STATE to track just transaction state (Is there an active transaction? " + "Does it have any data? etc.); CHARACTERISTICS to track transaction " + "state and report all statements needed to start a transaction with" + "the same characteristics (isolation level, read only/read write," + "snapshot - but not any work done / data modified within the " + "transaction).", + SESSION_VAR(session_track_transaction_info), + CMD_LINE(REQUIRED_ARG), session_track_transaction_info_names, + DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), + ON_UPDATE(update_session_track_tx_info)); + + static bool update_session_track_state_change(sys_var *self, THD *thd, enum_var_type type) { DBUG_ENTER("update_session_track_state_change"); - DBUG_RETURN(thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)->update(thd)); + DBUG_RETURN(thd->session_tracker.get_tracker(SESSION_STATE_CHANGE_TRACKER)-> + update(thd, NULL)); } static Sys_var_mybool Sys_session_track_state_change( "session_track_state_change", - "Track changes to the 'session state'.", + "Track changes to the session state.", SESSION_VAR(session_track_state_change), CMD_LINE(OPT_ARG), DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(update_session_track_state_change)); + +#endif //EMBEDDED_LIBRARY diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index dbe84d3efcc..6f17e768d95 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -567,6 +567,7 @@ public: { DBUG_ASSERT(FALSE); } }; +#ifndef EMBEDDED_LIBRARY class Sys_var_sesvartrack: public Sys_var_charptr_base { public: @@ -602,7 +603,7 @@ public: } bool session_update(THD *thd, set_var *var) { - return sysvartrack_update(thd); + return sysvartrack_update(thd, var); } void session_save_default(THD *thd, set_var *var) { @@ -623,19 +624,17 @@ public: { DBUG_ASSERT(thd != NULL); size_t len= sysvartrack_value_len(thd); - char *res= 0; - char *buf= (char *)my_safe_alloca(len); - if (buf && !sysvartrack_value_construct(thd, buf, len)) + char *res= (char *)thd->alloc(len + sizeof(char *)); + if (res) { - size_t len= strlen(buf) + 1; - res= (char*) thd->alloc(len + sizeof(char *)); - if (res) - memcpy((*((char**) res)= res + sizeof(char *)), buf, len); - my_safe_afree(buf, len); + char *buf= res + sizeof(char *); + *((char**) res)= buf; + sysvartrack_value_construct(thd, buf, len); } return (uchar *)res; } }; +#endif //EMBEDDED_LIBRARY class Sys_var_proxy_user: public sys_var @@ -2079,7 +2078,47 @@ public: if (var->type == OPT_SESSION && Sys_var_enum::session_update(thd, var)) return TRUE; if (var->type == OPT_DEFAULT || !thd->in_active_multi_stmt_transaction()) + { +#ifndef EMBEDDED_LIBRARY + Transaction_state_tracker *tst= NULL; + + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + tst= (Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER); +#endif //EMBEDDED_LIBRARY + thd->tx_isolation= (enum_tx_isolation) var->save_result.ulonglong_value; + +#ifndef EMBEDDED_LIBRARY + if (var->type == OPT_DEFAULT) + { + enum enum_tx_isol_level l; + switch (thd->tx_isolation) { + case ISO_READ_UNCOMMITTED: + l= TX_ISOL_UNCOMMITTED; + break; + case ISO_READ_COMMITTED: + l= TX_ISOL_COMMITTED; + break; + case ISO_REPEATABLE_READ: + l= TX_ISOL_REPEATABLE; + break; + case ISO_SERIALIZABLE: + l= TX_ISOL_SERIALIZABLE; + break; + default: + DBUG_ASSERT(0); + return TRUE; + } + if (tst) + tst->set_isol_level(thd, l); + } + else if (tst) + { + tst->set_isol_level(thd, TX_ISOL_INHERIT); + } +#endif //EMBEDDED_LIBRARY + } return FALSE; } }; diff --git a/sql/transaction.cc b/sql/transaction.cc index 8b188709ce6..d728ea25b65 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -25,6 +25,43 @@ #include "debug_sync.h" // DEBUG_SYNC #include "sql_acl.h" + +#ifndef EMBEDDED_LIBRARY +/** + Helper: Tell tracker (if any) that transaction ended. +*/ +static void trans_track_end_trx(THD *thd) +{ + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + { + ((Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER))->end_trx(thd); + } +} + + +/** + Helper: transaction ended, SET TRANSACTION one-shot variables + revert to session values. Let the transaction state tracker know. +*/ +void trans_reset_one_shot_chistics(THD *thd) +{ + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + { + Transaction_state_tracker *tst= (Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER); + + tst->set_read_flags(thd, TX_READ_INHERIT); + tst->set_isol_level(thd, TX_ISOL_INHERIT); + } + + thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; + thd->tx_read_only= thd->variables.tx_read_only; +} +#else +#define trans_track_end_trx(A) do{}while(0) +#endif //EMBEDDED_LIBRARY + /* Conditions under which the transaction state must not change. */ static bool trans_check(THD *thd) { @@ -125,11 +162,20 @@ static bool xa_trans_force_rollback(THD *thd) bool trans_begin(THD *thd, uint flags) { int res= FALSE; +#ifndef EMBEDDED_LIBRARY + Transaction_state_tracker *tst= NULL; +#endif //EMBEDDED_LIBRARY DBUG_ENTER("trans_begin"); if (trans_check(thd)) DBUG_RETURN(TRUE); +#ifndef EMBEDDED_LIBRARY + if (thd->variables.session_track_transaction_info > TX_TRACK_NONE) + tst= (Transaction_state_tracker *) + thd->session_tracker.get_tracker(TRANSACTION_INFO_TRACKER); +#endif //EMBEDDED_LIBRARY + thd->locked_tables_list.unlock_locked_tables(thd); DBUG_ASSERT(!thd->locked_tables_mode); @@ -172,7 +218,13 @@ bool trans_begin(THD *thd, uint flags) DBUG_ASSERT(!((flags & MYSQL_START_TRANS_OPT_READ_ONLY) && (flags & MYSQL_START_TRANS_OPT_READ_WRITE))); if (flags & MYSQL_START_TRANS_OPT_READ_ONLY) + { thd->tx_read_only= true; +#ifndef EMBEDDED_LIBRARY + if (tst) + tst->set_read_flags(thd, TX_READ_ONLY); +#endif //EMBEDDED_LIBRARY + } else if (flags & MYSQL_START_TRANS_OPT_READ_WRITE) { /* @@ -189,6 +241,14 @@ bool trans_begin(THD *thd, uint flags) DBUG_RETURN(true); } thd->tx_read_only= false; + /* + This flags that tx_read_only was set explicitly, rather than + just from the session's default. + */ +#ifndef EMBEDDED_LIBRARY + if (tst) + tst->set_read_flags(thd, TX_READ_WRITE); +#endif //EMBEDDED_LIBRARY } #ifdef WITH_WSREP @@ -203,9 +263,20 @@ bool trans_begin(THD *thd, uint flags) thd->server_status|= SERVER_STATUS_IN_TRANS_READONLY; DBUG_PRINT("info", ("setting SERVER_STATUS_IN_TRANS")); +#ifndef EMBEDDED_LIBRARY + if (tst) + tst->add_trx_state(thd, TX_EXPLICIT); +#endif //EMBEDDED_LIBRARY + /* ha_start_consistent_snapshot() relies on OPTION_BEGIN flag set. */ if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) + { +#ifndef EMBEDDED_LIBRARY + if (tst) + tst->add_trx_state(thd, TX_WITH_SNAPSHOT); +#endif //EMBEDDED_LIBRARY res= ha_start_consistent_snapshot(thd); + } DBUG_RETURN(MY_TEST(res)); } @@ -255,6 +326,8 @@ bool trans_commit(THD *thd) thd->transaction.all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT; thd->lex->start_transaction_opt= 0; + trans_track_end_trx(thd); + DBUG_RETURN(MY_TEST(res)); } @@ -308,8 +381,9 @@ bool trans_commit_implicit(THD *thd) @@session.completion_type since it's documented to not have any effect on implicit commit. */ - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); + + trans_track_end_trx(thd); DBUG_RETURN(res); } @@ -349,6 +423,8 @@ bool trans_rollback(THD *thd) thd->transaction.all.m_unsafe_rollback_flags&= ~THD_TRANS::DID_WAIT; thd->lex->start_transaction_opt= 0; + trans_track_end_trx(thd); + DBUG_RETURN(MY_TEST(res)); } @@ -396,6 +472,8 @@ bool trans_rollback_implicit(THD *thd) /* Rollback should clear transaction_rollback_request flag. */ DBUG_ASSERT(! thd->transaction_rollback_request); + trans_track_end_trx(thd); + DBUG_RETURN(MY_TEST(res)); } @@ -434,8 +512,7 @@ bool trans_commit_stmt(THD *thd) res= ha_commit_trans(thd, FALSE); if (! thd->in_active_multi_stmt_transaction()) { - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; + trans_reset_one_shot_chistics(thd); if (WSREP_ON) wsrep_post_commit(thd, FALSE); } @@ -487,10 +564,7 @@ bool trans_rollback_stmt(THD *thd) wsrep_register_hton(thd, FALSE); ha_rollback_trans(thd, FALSE); if (! thd->in_active_multi_stmt_transaction()) - { - thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; - thd->tx_read_only= thd->variables.tx_read_only; - } + trans_reset_one_shot_chistics(thd); } (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); @@ -912,6 +986,8 @@ bool trans_xa_commit(THD *thd) xid_cache_delete(thd, &thd->transaction.xid_state); thd->transaction.xid_state.xa_state= XA_NOTR; + trans_track_end_trx(thd); + DBUG_RETURN(res); } @@ -968,5 +1044,7 @@ bool trans_xa_rollback(THD *thd) xid_cache_delete(thd, &thd->transaction.xid_state); thd->transaction.xid_state.xa_state= XA_NOTR; + trans_track_end_trx(thd); + DBUG_RETURN(res); } diff --git a/sql/transaction.h b/sql/transaction.h index 54b25f1de2a..040f1a453cd 100644 --- a/sql/transaction.h +++ b/sql/transaction.h @@ -44,4 +44,10 @@ bool trans_xa_prepare(THD *thd); bool trans_xa_commit(THD *thd); bool trans_xa_rollback(THD *thd); +#ifndef EMBEDDED_LIBRARY +void trans_reset_one_shot_chistics(THD *thd); +#else +#define trans_reset_one_shot_chistics(A) do{}while(0) +#endif //EMBEDDED_LIBRARY + #endif /* TRANSACTION_H */ From 6dfa1d374e8c4d3a06e25ceb78cc6571418363ab Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Tue, 9 Aug 2016 15:49:30 +0200 Subject: [PATCH 6/8] MDEV-8931: (server part of) session state tracking Postreview fixes. New MySQL tests fixes. --- include/mysql.h.pp | 2 +- include/mysql_com.h | 4 +- libmysqld/lib_sql.cc | 3 +- mysql-test/r/mysqld--help.result | 8 +- ...ession_track_system_variables_basic.result | 22 +- .../r/sysvars_server_notembedded.result | 8 +- sql/session_tracker.cc | 272 ++++++++++-------- sql/session_tracker.h | 4 +- sql/sp_head.cc | 6 +- sql/sql_string.h | 10 - sql/sys_vars.cc | 8 +- 11 files changed, 196 insertions(+), 151 deletions(-) diff --git a/include/mysql.h.pp b/include/mysql.h.pp index 0ef3403626c..e1a0901cee8 100644 --- a/include/mysql.h.pp +++ b/include/mysql.h.pp @@ -95,7 +95,7 @@ enum enum_session_state_type SESSION_TRACK_GTIDS, SESSION_TRACK_TRANSACTION_CHARACTERISTICS, SESSION_TRACK_TRANSACTION_STATE, - SESSION_TRACK_END + SESSION_TRACK_always_at_the_end }; my_bool my_net_init(NET *net, Vio* vio, void *thd, unsigned int my_flags); void my_net_local_init(NET *net); diff --git a/include/mysql_com.h b/include/mysql_com.h index 16af9849759..82f3b9f62ba 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -559,13 +559,13 @@ enum enum_session_state_type SESSION_TRACK_GTIDS, SESSION_TRACK_TRANSACTION_CHARACTERISTICS, /* Transaction chistics */ SESSION_TRACK_TRANSACTION_STATE, /* Transaction state */ - SESSION_TRACK_END /* must be last */ + SESSION_TRACK_always_at_the_end /* must be last */ }; #define SESSION_TRACK_BEGIN SESSION_TRACK_SYSTEM_VARIABLES #define IS_SESSION_STATE_TYPE(T) \ - (((int)(T) >= SESSION_TRACK_BEGIN) && ((T) < SESSION_TRACK_END)) + (((int)(T) >= SESSION_TRACK_BEGIN) && ((T) < SESSION_TRACK_always_at_the_end)) #define net_new_transaction(net) ((net)->pkt_nr=0) diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index aeee99ebe4d..097cde04b43 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -1172,8 +1172,7 @@ bool net_send_ok(THD *thd, uint server_status, uint statement_warn_count, ulonglong affected_rows, ulonglong id, const char *message, - bool unused1, - bool unused2) + bool, bool) { DBUG_ENTER("emb_net_send_ok"); MYSQL_DATA *data; diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result index 176e8186798..ad57cc6fe5e 100644 --- a/mysql-test/r/mysqld--help.result +++ b/mysql-test/r/mysqld--help.result @@ -909,7 +909,11 @@ The following options may be given as the first argument: --session-track-state-change Track changes to the session state. --session-track-system-variables=name - Track changes in registered system variables. + Track changes in registered system variables. For + compatibility with MySQL defaults this variable should be + set to "autocommit, character_set_client, + character_set_connection, character_set_results, + time_zone" --session-track-transaction-info=name Track changes to the transaction attributes. OFF to disable; STATE to track just transaction state (Is there @@ -1403,7 +1407,7 @@ secure-file-priv (No default value) server-id 1 session-track-schema TRUE session-track-state-change FALSE -session-track-system-variables autocommit,character_set_client,character_set_connection,character_set_results,time_zone +session-track-system-variables session-track-transaction-info OFF show-slave-auth-info FALSE silent-startup FALSE diff --git a/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result b/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result index 78ca8ca4ad1..7162e40ef6b 100644 --- a/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result +++ b/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result @@ -5,25 +5,25 @@ # Global - default SELECT @@global.session_track_system_variables; @@global.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone + # Session - default SELECT @@session.session_track_system_variables; @@session.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone + # via INFORMATION_SCHEMA.GLOBAL_VARIABLES SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; VARIABLE_NAME VARIABLE_VALUE SESSION_TRACK_SCHEMA ON SESSION_TRACK_STATE_CHANGE OFF -SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SESSION_TRACK_SYSTEM_VARIABLES SESSION_TRACK_TRANSACTION_INFO OFF # via INFORMATION_SCHEMA.SESSION_VARIABLES SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME; VARIABLE_NAME VARIABLE_VALUE SESSION_TRACK_SCHEMA ON SESSION_TRACK_STATE_CHANGE OFF -SESSION_TRACK_SYSTEM_VARIABLES autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SESSION_TRACK_SYSTEM_VARIABLES SESSION_TRACK_TRANSACTION_INFO OFF SET @global_saved_tmp = @@global.session_track_system_variables; @@ -34,7 +34,7 @@ SELECT @@global.session_track_system_variables; autocommit SELECT @@session.session_track_system_variables; @@session.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone + # Altering session variable's value SET @@session.session_track_system_variables='autocommit'; @@ -72,25 +72,25 @@ SET @@session.session_track_system_variables = DEFAULT; SELECT @@global.session_track_system_variables; @@global.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone + SELECT @@session.session_track_system_variables; @@session.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone + # Variables' values in a new session (con2). connect con2,"127.0.0.1",root,,test,$MASTER_MYPORT,; SELECT @@global.session_track_system_variables; @@global.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone + SELECT @@session.session_track_system_variables; @@session.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone + # Altering session should not affect global. SET @@session.session_track_system_variables = 'sql_mode'; SELECT @@global.session_track_system_variables; @@global.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone + SELECT @@session.session_track_system_variables; @@session.session_track_system_variables sql_mode @@ -104,7 +104,7 @@ SELECT @@global.session_track_system_variables; sql_mode SELECT @@session.session_track_system_variables; @@session.session_track_system_variables -autocommit,character_set_client,character_set_connection,character_set_results,time_zone + # Switching to the default connection. connection default; diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 3a73ca07402..e422be7287e 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -3818,13 +3818,13 @@ ENUM_VALUE_LIST OFF,ON READ_ONLY NO COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME SESSION_TRACK_SYSTEM_VARIABLES -SESSION_VALUE autocommit,character_set_client,character_set_connection,character_set_results,time_zone -GLOBAL_VALUE autocommit,character_set_client,character_set_connection,character_set_results,time_zone +SESSION_VALUE +GLOBAL_VALUE GLOBAL_VALUE_ORIGIN COMPILE-TIME -DEFAULT_VALUE autocommit,character_set_client,character_set_connection,character_set_results,time_zone +DEFAULT_VALUE VARIABLE_SCOPE SESSION VARIABLE_TYPE VARCHAR -VARIABLE_COMMENT Track changes in registered system variables. +VARIABLE_COMMENT Track changes in registered system variables. For compatibility with MySQL defaults this variable should be set to "autocommit, character_set_client, character_set_connection, character_set_results, time_zone" NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc index bd641ab8d03..3272d2a41f0 100644 --- a/sql/session_tracker.cc +++ b/sql/session_tracker.cc @@ -144,16 +144,18 @@ private: return res; } - uchar* operator[](ulong idx) - { - return my_hash_element(&m_registered_sysvars, idx); - } bool insert(sysvar_node_st *node, const sys_var *svar, myf mem_flag); + void reinit(); void reset(); + inline bool is_enabled() + { + return track_all || m_registered_sysvars.records; + } void copy(vars_list* from, THD *thd); bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error, - CHARSET_INFO *char_set, bool session_created); + CHARSET_INFO *char_set, bool take_mutex); bool construct_var_list(char *buf, size_t buf_len); + bool store(THD *thd, String *buf); }; /** Two objects of vars_list type are maintained to manage @@ -217,9 +219,13 @@ public: static uchar *sysvars_get_key(const char *entry, size_t *length, my_bool not_used __attribute__((unused))); + // hash iterators static my_bool name_array_filler(void *ptr, void *data_ptr); + static my_bool store_variable(void *ptr, void *data_ptr); + static my_bool reset_variable(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); + CHARSET_INFO *char_set, bool take_mutex); }; @@ -284,7 +290,7 @@ public: static const unsigned int EXTRA_ALLOC= 1024; -void Session_sysvars_tracker::vars_list::reset() +void Session_sysvars_tracker::vars_list::reinit() { buffer_length= 0; track_all= 0; @@ -304,7 +310,7 @@ void Session_sysvars_tracker::vars_list::reset() void Session_sysvars_tracker::vars_list::copy(vars_list* from, THD *thd) { - reset(); + reinit(); track_all= from->track_all; free_hash(); buffer_length= from->buffer_length; @@ -331,7 +337,7 @@ bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node, if (!(node= (sysvar_node_st *) my_malloc(sizeof(sysvar_node_st), MYF(MY_WME | mem_flag)))) { - reset(); + reinit(); return true; } } @@ -345,7 +351,7 @@ bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node, if (!search((sys_var *)svar)) { //EOF (error is already reported) - reset(); + reinit(); return true; } } @@ -367,9 +373,7 @@ bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node, in case of invalid/duplicate values. @param char_set [IN] charecter set information used for string manipulations. - @param session_created [IN] bool variable which says if the parse is - already executed once. The mutex on variables - is not acquired if this variable is false. + @param take_mutex [IN] take LOCK_plugin @return true Error @@ -379,11 +383,12 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error, CHARSET_INFO *char_set, - bool session_created) + bool take_mutex) { const char separator= ','; char *token, *lasts= NULL; size_t rest= var_list.length; + reinit(); if (!var_list.str || var_list.length == 0) { @@ -408,7 +413,7 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, token value. Hence the mutex is handled here to avoid a performance overhead. */ - if (!thd || session_created) + if (!thd || take_mutex) mysql_mutex_lock(&LOCK_plugin); for (;;) { @@ -429,12 +434,17 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, /* Remove leading/trailing whitespace. */ trim_whitespace(char_set, &var); - if ((svar= find_sys_var_ex(thd, var.str, var.length, throw_error, true))) + if(!strcmp(var.str,(const char *)"*")) + { + track_all= true; + } + else if ((svar= + find_sys_var_ex(thd, var.str, var.length, throw_error, true))) { if (insert(NULL, svar, m_mem_flag) == TRUE) goto error; } - else if (throw_error && session_created && thd) + else if (throw_error && thd) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WRONG_VALUE_FOR_VAR, @@ -449,13 +459,13 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, else break; } - if (!thd || session_created) + if (!thd || take_mutex) mysql_mutex_unlock(&LOCK_plugin); return false; error: - if (!thd || session_created) + if (!thd || take_mutex) mysql_mutex_unlock(&LOCK_plugin); return true; } @@ -465,7 +475,7 @@ bool Session_sysvars_tracker::check_var_list(THD *thd, LEX_STRING var_list, bool throw_error, CHARSET_INFO *char_set, - bool session_created) + bool take_mutex) { const char separator= ','; char *token, *lasts= NULL; @@ -485,11 +495,10 @@ bool Session_sysvars_tracker::check_var_list(THD *thd, token value. Hence the mutex is handled here to avoid a performance overhead. */ - if (!thd || session_created) + if (!thd || take_mutex) mysql_mutex_lock(&LOCK_plugin); for (;;) { - sys_var *svar; LEX_STRING var; lasts= (char *) memchr(token, separator, rest); @@ -506,9 +515,10 @@ bool Session_sysvars_tracker::check_var_list(THD *thd, /* Remove leading/trailing whitespace. */ trim_whitespace(char_set, &var); - if (!(svar= find_sys_var_ex(thd, var.str, var.length, throw_error, true))) + if(!strcmp(var.str,(const char *)"*") && + !find_sys_var_ex(thd, var.str, var.length, throw_error, true)) { - if (throw_error && session_created && thd) + if (throw_error && take_mutex && thd) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WRONG_VALUE_FOR_VAR, @@ -517,7 +527,7 @@ bool Session_sysvars_tracker::check_var_list(THD *thd, } else { - if (!thd || session_created) + if (!thd || take_mutex) mysql_mutex_unlock(&LOCK_plugin); return true; } @@ -528,7 +538,7 @@ bool Session_sysvars_tracker::check_var_list(THD *thd, else break; } - if (!thd || session_created) + if (!thd || take_mutex) mysql_mutex_unlock(&LOCK_plugin); return false; @@ -605,7 +615,10 @@ bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf, my_hash_iterate(&m_registered_sysvars, &name_array_filler, &data); DBUG_ASSERT(data.idx <= m_registered_sysvars.records); - + /* + We check number of records again here because number of variables + could be reduced in case of plugin unload. + */ if (m_registered_sysvars.records == 0) { mysql_mutex_unlock(&LOCK_plugin); @@ -710,7 +723,7 @@ 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(); + tool_list->reinit(); if (tool_list->parse_var_list(thd, var->save_result.string_value, true, thd->charset(), true)) return true; @@ -719,6 +732,83 @@ bool Session_sysvars_tracker::update(THD *thd, set_var *var) } +/* + Function and structure to support storing variables from hash to the buffer. +*/ + +struct st_store_variable_param +{ + THD *thd; + String *buf; +}; + +my_bool Session_sysvars_tracker::store_variable(void *ptr, void *data_ptr) +{ + Session_sysvars_tracker::sysvar_node_st *node= + (Session_sysvars_tracker::sysvar_node_st *)ptr; + if (node->m_changed) + { + THD *thd= ((st_store_variable_param *)data_ptr)->thd; + String *buf= ((st_store_variable_param *)data_ptr)->buf; + char val_buf[SHOW_VAR_FUNC_BUFF_SIZE]; + SHOW_VAR show; + CHARSET_INFO *charset; + size_t val_length, length; + mysql_mutex_lock(&LOCK_plugin); + if (!*node->test_load) + { + mysql_mutex_unlock(&LOCK_plugin); + return false; + } + sys_var *svar= node->m_svar; + bool is_plugin= svar->cast_pluginvar(); + if (!is_plugin) + mysql_mutex_unlock(&LOCK_plugin); + + /* As its always system variable. */ + show.type= SHOW_SYS; + show.name= svar->name.str; + show.value= (char *) svar; + + const char *value= get_one_variable(thd, &show, OPT_SESSION, SHOW_SYS, NULL, + &charset, val_buf, &val_length); + if (is_plugin) + mysql_mutex_unlock(&LOCK_plugin); + + length= net_length_size(svar->name.length) + + svar->name.length + + net_length_size(val_length) + + val_length; + + compile_time_assert(SESSION_TRACK_SYSTEM_VARIABLES < 251); + if (unlikely((1 + net_length_size(length) + length + buf->length() >= + MAX_PACKET_LENGTH) || + buf->reserve(1 + net_length_size(length) + length, + EXTRA_ALLOC))) + return true; + + + /* Session state type (SESSION_TRACK_SYSTEM_VARIABLES) */ + buf->q_append((char)SESSION_TRACK_SYSTEM_VARIABLES); + + /* Length of the overall entity. */ + buf->q_net_store_length((ulonglong)length); + + /* System variable's name (length-encoded string). */ + buf->q_net_store_data((const uchar*)svar->name.str, svar->name.length); + + /* System variable's value (length-encoded string). */ + buf->q_net_store_data((const uchar*)value, val_length); + } + return false; +} + +bool Session_sysvars_tracker::vars_list::store(THD *thd, String *buf) +{ + st_store_variable_param data= {thd, buf}; + return my_hash_iterate(&m_registered_sysvars, &store_variable, &data); +} + /** Store the data for changed system variables in the specified buffer. Once the data is stored, we reset the flags related to state-change @@ -733,62 +823,11 @@ bool Session_sysvars_tracker::update(THD *thd, set_var *var) bool Session_sysvars_tracker::store(THD *thd, String *buf) { - char val_buf[SHOW_VAR_FUNC_BUFF_SIZE]; - SHOW_VAR show; - const char *value; - sysvar_node_st *node; - CHARSET_INFO *charset; - size_t val_length, length; - int idx= 0; + if (!orig_list->is_enabled()) + return false; - /* As its always system variable. */ - show.type= SHOW_SYS; - - while ((node= (sysvar_node_st *) (*orig_list)[idx])) - { - if (node->m_changed) - { - mysql_mutex_lock(&LOCK_plugin); - if (!*node->test_load) - { - mysql_mutex_unlock(&LOCK_plugin); - continue; - } - sys_var *svar= node->m_svar; - show.name= svar->name.str; - show.value= (char *) svar; - - value= get_one_variable(thd, &show, OPT_SESSION, SHOW_SYS, NULL, - &charset, val_buf, &val_length); - mysql_mutex_unlock(&LOCK_plugin); - - length= net_length_size(svar->name.length) + - svar->name.length + - net_length_size(val_length) + - val_length; - - compile_time_assert(SESSION_TRACK_SYSTEM_VARIABLES < 251); - 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_append((char)SESSION_TRACK_SYSTEM_VARIABLES); - - /* Length of the overall entity. */ - buf->q_net_store_length((ulonglong)length); - - /* System variable's name (length-encoded string). */ - buf->q_net_store_data((const uchar*)svar->name.str, svar->name.length); - - /* System variable's value (length-encoded string). */ - buf->q_net_store_data((const uchar*)value, val_length); - } - ++ idx; - } + if (orig_list->store(thd, buf)) + return true; reset(); @@ -811,7 +850,8 @@ 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->insert_or_search(node, svar)))) + if (orig_list->is_enabled() && + (node= (sysvar_node_st *) (orig_list->insert_or_search(node, svar)))) { node->m_changed= true; State_tracker::mark_as_changed(thd, var); @@ -838,20 +878,28 @@ uchar *Session_sysvars_tracker::sysvars_get_key(const char *entry, } +/* Function to support resetting hash nodes for the variables */ + +my_bool Session_sysvars_tracker::reset_variable(void *ptr, + void *data_ptr) +{ + ((Session_sysvars_tracker::sysvar_node_st *)ptr)->m_changed= false; + return false; +} + +void Session_sysvars_tracker::vars_list::reset() +{ + my_hash_iterate(&m_registered_sysvars, &reset_variable, NULL); +} + /** Prepare/reset the m_registered_sysvars hash for next statement. */ void Session_sysvars_tracker::reset() { - sysvar_node_st *node; - int idx= 0; - while ((node= (sysvar_node_st *) (*orig_list)[idx])) - { - node->m_changed= false; - ++ idx; - } + orig_list->reset(); m_changed= false; } @@ -931,7 +979,7 @@ bool Current_schema_tracker::store(THD *thd, String *buf) compile_time_assert(NAME_LEN < 251); DBUG_ASSERT(length < 251); if (unlikely((1 + 1 + length + buf->length() >= MAX_PACKET_LENGTH) || - buf->prep_alloc(1 + 1 + length, EXTRA_ALLOC))) + buf->reserve(1 + 1 + length, EXTRA_ALLOC))) return true; /* Session state type (SESSION_TRACK_SCHEMA) */ @@ -1034,26 +1082,25 @@ 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))) + buf->reserve(11, EXTRA_ALLOC))) return true; - *(to++)= (char)SESSION_TRACK_TRANSACTION_STATE; + buf->q_append((char)SESSION_TRACK_TRANSACTION_STATE); - to= net_store_length((uchar *) to, (ulonglong) 9); - to= net_store_length((uchar *) to, (ulonglong) 8); + buf->q_append((char)9); // whole packet length + buf->q_append((char)8); // results length - *(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' : '_'; + buf->q_append((char)((tx_curr_state & TX_EXPLICIT) ? 'T' : + ((tx_curr_state & TX_IMPLICIT) ? 'I' : '_'))); + buf->q_append((char)((tx_curr_state & TX_READ_UNSAFE) ? 'r' : '_')); + buf->q_append((char)(((tx_curr_state & TX_READ_TRX) || + (tx_curr_state & TX_WITH_SNAPSHOT)) ? 'R' : '_')); + buf->q_append((char)((tx_curr_state & TX_WRITE_UNSAFE) ? 'w' : '_')); + buf->q_append((char)((tx_curr_state & TX_WRITE_TRX) ? 'W' : '_')); + buf->q_append((char)((tx_curr_state & TX_STMT_UNSAFE) ? 's' : '_')); + buf->q_append((char)((tx_curr_state & TX_RESULT_SET) ? 'S' : '_')); + buf->q_append((char)((tx_curr_state & TX_LOCKED_TABLES) ? 'L' : '_')); } /* CHARACTERISTICS -- How to restart the transaction */ @@ -1066,7 +1113,7 @@ bool Transaction_state_tracker::store(THD *thd, String *buf) /* 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))) + buf->reserve(1 + 1 + 1, EXTRA_ALLOC))) return true; compile_time_assert(SESSION_TRACK_TRANSACTION_CHARACTERISTICS < 251); @@ -1503,7 +1550,7 @@ bool Session_state_change_tracker::update(THD *thd, set_var *) bool Session_state_change_tracker::store(THD *thd, String *buf) { if (unlikely((1 + 1 + 1 + buf->length() >= MAX_PACKET_LENGTH) || - buf->prep_alloc(1 + 1 + 1, EXTRA_ALLOC))) + buf->reserve(1 + 1 + 1, EXTRA_ALLOC))) return true; compile_time_assert(SESSION_TRACK_STATE_CHANGE < 251); @@ -1550,9 +1597,10 @@ bool Session_state_change_tracker::is_state_changed(THD *) Session_tracker::Session_tracker() { /* track data ID fit into one byte in net coding */ - compile_time_assert(SESSION_TRACK_END < 251); + compile_time_assert(SESSION_TRACK_always_at_the_end < 251); /* one tracker could serv several tracking data */ - compile_time_assert((uint)SESSION_TRACK_END >= (uint)SESSION_TRACKER_END); + compile_time_assert((uint)SESSION_TRACK_always_at_the_end >= + (uint)SESSION_TRACKER_END); for (int i= 0; i < SESSION_TRACKER_END; i++) m_trackers[i]= NULL; @@ -1648,7 +1696,7 @@ void Session_tracker::store(THD *thd, String *buf) if ((size= net_length_size(length)) != 1) { - if (buf->prep_alloc(size - 1, EXTRA_ALLOC)) + if (buf->reserve(size - 1, EXTRA_ALLOC)) { buf->length(start); // it is safer to have 0-length block in case of error return; diff --git a/sql/session_tracker.h b/sql/session_tracker.h index 431726f03ed..3f73b5dc705 100644 --- a/sql/session_tracker.h +++ b/sql/session_tracker.h @@ -62,8 +62,8 @@ 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. + @note: it is a cache of the corresponding thd->variables.session_track_xxx + variable */ bool m_enabled; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index c344a6c6ed8..41006f07a0a 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2981,8 +2981,10 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, #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 there was instruction which changed tracking state, + the result of changed tracking state send to client in OK packed. + So it changes result sent to client and probably can be different + independent on query text. So we can't cache such results. */ if ((thd->client_capabilities & CLIENT_SESSION_TRACK) && (thd->server_status & SERVER_SESSION_STATE_CHANGED)) diff --git a/sql/sql_string.h b/sql/sql_string.h index feab8070cd2..f53015fbd6b 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -566,16 +566,6 @@ 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) { diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 462bfe52741..a047823551b 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -5393,10 +5393,12 @@ static Sys_var_ulong Sys_log_tc_size( static Sys_var_sesvartrack Sys_track_session_sys_vars( "session_track_system_variables", - "Track changes in registered system variables.", + "Track changes in registered system variables. " + "For compatibility with MySQL defaults this variable should be set to " + "\"autocommit, character_set_client, character_set_connection, " + "character_set_results, time_zone\"", CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET, - DEFAULT("autocommit,character_set_client,character_set_connection," - "character_set_results,time_zone"), + DEFAULT(""), NO_MUTEX_GUARD); static bool update_session_track_schema(sys_var *self, THD *thd, From 7b86fda0c9b6b9772c20005fb135c95277083f1d Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Sun, 28 Aug 2016 09:44:49 +0200 Subject: [PATCH 7/8] Fixed length of codding of COM_MULTI parts. --- sql/sql_parse.cc | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 8dc34c2dfe2..c76e22a2e57 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1476,13 +1476,16 @@ uint maria_multi_check(THD *thd, char *packet, uint packet_length) DBUG_ENTER("maria_multi_check"); while (packet_length) { + char *packet_start= packet; + size_t subpacket_length= net_field_length((uchar **)&packet_start); + uint length_length= packet_start - packet; // length of command + 3 bytes where that length was stored - uint subpacket_length= (uint3korr(packet) + 3); - DBUG_PRINT("info", ("sub-packet length: %d command: %x", - subpacket_length, packet[3])); + DBUG_PRINT("info", ("sub-packet length: %ld + %d command: %x", + (ulong)subpacket_length, length_length, + packet_start[3])); - if (subpacket_length == 3 || - subpacket_length > packet_length) + if (subpacket_length == 0 || + (subpacket_length + length_length) > packet_length) { my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR), MYF(0)); @@ -1490,8 +1493,8 @@ uint maria_multi_check(THD *thd, char *packet, uint packet_length) } counter++; - packet+= subpacket_length; - packet_length-= subpacket_length; + packet= packet_start + subpacket_length; + packet_length-= (subpacket_length + length_length); } DBUG_RETURN(counter); } @@ -2231,8 +2234,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; { + char *packet_start= packet; /* We have to store next length because it will be destroyed by '\0' */ - uint next_subpacket_length= uint3korr(packet); + size_t next_subpacket_length= net_field_length((uchar **)&packet_start); + uint next_length_length= packet_start - packet; unsigned char *readbuff= net->buff; if (net_allocate_new_packet(net, thd, MYF(0))) @@ -2246,13 +2251,19 @@ bool dispatch_command(enum enum_server_command command, THD *thd, while (packet_length) { current_com++; - uint subpacket_length= next_subpacket_length + 3; + size_t subpacket_length= next_subpacket_length + next_length_length; + uint length_length= next_length_length; if (subpacket_length < packet_length) - next_subpacket_length= uint3korr(packet + subpacket_length); + { + packet_start= packet + subpacket_length; + next_subpacket_length= net_field_length((uchar**)&packet_start); + next_length_length= packet_start - (packet + subpacket_length); + } /* safety like in do_command() */ packet[subpacket_length]= '\0'; - enum enum_server_command subcommand= fetch_command(thd, (packet + 3)); + enum enum_server_command subcommand= + fetch_command(thd, (packet + length_length)); if (server_command_flags[subcommand] & CF_NO_COM_MULTI) { @@ -2260,8 +2271,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, goto com_multi_end; } - if (dispatch_command(subcommand, thd, packet + (1 + 3), - subpacket_length - (1 + 3), TRUE, + if (dispatch_command(subcommand, thd, packet + (1 + length_length), + subpacket_length - (1 + length_length), TRUE, (current_com != counter))) { DBUG_ASSERT(thd->is_error()); From 1eb58ff3b8569d7dad1f5c180a5e55683e53d205 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Wed, 31 Aug 2016 20:33:28 +0300 Subject: [PATCH 8/8] Update mysql-test/r/mysqld--help,win.rdiff Recent commits has added new variables and changed the default for server-id, which caused the patch in rdiff to no longer apply. --- mysql-test/r/mysqld--help,win.rdiff | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/mysqld--help,win.rdiff b/mysql-test/r/mysqld--help,win.rdiff index ac10f37b5be..5dabf58208d 100644 --- a/mysql-test/r/mysqld--help,win.rdiff +++ b/mysql-test/r/mysqld--help,win.rdiff @@ -23,9 +23,9 @@ + --shared-memory Enable the shared memory + --shared-memory-base-name=name + Base name of shared memory - --show-slave-auth-info - Show user and password in SHOW SLAVE HOSTS on this - master. + --session-track-schema + Track changes to the default schema. + (Defaults to on; use --skip-session-track-schema to disable.) @@ -1015,6 +1018,10 @@ Log slow queries to given log file. Defaults logging to 'hostname'-slow.log. Must be enabled to activate other @@ -103,12 +103,12 @@ @@ -1387,6 +1381,8 @@ secure-auth TRUE secure-file-priv (No default value) - server-id 0 + server-id 1 +shared-memory FALSE +shared-memory-base-name MYSQL - show-slave-auth-info FALSE - silent-startup FALSE - skip-grant-tables TRUE + session-track-schema TRUE + session-track-state-change FALSE + session-track-system-variables @@ -1411,6 +1407,7 @@ slave-type-conversions slow-launch-time 2