#At file:///home/bar/mysql-bzr/mysql-5.5-bugteam.b57306/ based on revid:vvaintroub@stripped3mkujaucde
3121 Alexander Barkov 2010-11-16
Bug#57306 SHOW PROCESSLIST does not display string literals well.
Problem: Extended characters outside of ASCII range where not displayed
properly in SHOW PROCESSLIST, because thd_info->query was always sent as
system_character_set (utf8). This was wrong, because query buffer
is never converted to utf8 - it is always have client character set.
Fix: sending query buffer using query character set
@ sql/sql_class.cc
@ sql/sql_class.h
Introducing a new class CSET_STRING, a LEX_STRING with character set,
with automatic initialization in constructor.
Adding set_query(&CSET_STRING)
Adding reset_query(), to use instead of set_query(0, NULL).
@ sql/event_data_objects.cc
Using reset_query()
@ sql/log_event.cc
Using reset_query()
Adding charset argument to set_query_and_id().
@ sql/slave.cc
Using reset_query().
@ sql/sp_head.cc
Changing backing up and restore code to use CSET_STRING.
@ sql/sql_audit.h
Using CSET_STRING.
In the "else" branch it's OK not to use
global_system_variables.character_set_client.
&my_charset_latin1, which is set in constructor, is fine
(verified with Sergey Vojtovich).
@ sql/sql_insert.cc
Using set_query() with proper character set: table_name is utf8.
@ sql/sql_parse.cc
Adding character set argument to set_query_and_id().
(This is the main point where thd->charset() is stored
into thd->query_string.cs, for use in "SHOW PROCESSLIST".)
Using reset_query().
@ sql/sql_prepare.cc
Storing client character set into thd->query_string.cs.
@ sql/sql_show.cc
Using CSET_STRING to fetch and send charset-aware query information
from threads.
@ storage/myisam/ha_myisam.cc
Using set_query() with proper character set: table_name is utf8.
@ mysql-test/r/show_check.result
@ mysql-test/t/show_check.test
Adding tests
modified:
mysql-test/r/show_check.result
mysql-test/t/show_check.test
sql/event_data_objects.cc
sql/log_event.cc
sql/slave.cc
sql/sp_head.cc
sql/sql_audit.h
sql/sql_class.cc
sql/sql_class.h
sql/sql_insert.cc
sql/sql_parse.cc
sql/sql_prepare.cc
sql/sql_show.cc
storage/myisam/ha_myisam.cc
=== modified file 'mysql-test/r/show_check.result'
--- a/mysql-test/r/show_check.result 2010-07-23 08:44:55 +0000
+++ b/mysql-test/r/show_check.result 2010-11-16 19:07:35 +0000
@@ -1514,3 +1514,27 @@ ALTER TABLE t1 CHARACTER SET = utf8;
COMMIT;
DROP TRIGGER t1_bi;
DROP TABLE t1;
+#
+# Bug#57306 SHOW PROCESSLIST does not display string literals well.
+#
+SET NAMES latin1;
+SELECT GET_LOCK('t', 1000);
+GET_LOCK('t', 1000)
+1
+SET NAMES latin1;
+SELECT GET_LOCK('t',1000) AS 'óóóó';;
+SHOW PROCESSLIST;
+Id User Host db Command Time State Info
+### root localhost test Query ### ### SHOW PROCESSLIST
+### root localhost test Query ### ### SELECT GET_LOCK('t',1000) AS 'óóóó'
+SET NAMES utf8;
+SHOW PROCESSLIST;
+Id User Host db Command Time State Info
+### root localhost test Query ### ### SHOW PROCESSLIST
+### root localhost test Query ### ### SELECT GET_LOCK('t',1000) AS 'óóóó'
+SELECT RELEASE_LOCK('t');
+RELEASE_LOCK('t')
+1
+óóóó
+1
+SET NAMES latin1;
=== modified file 'mysql-test/t/show_check.test'
--- a/mysql-test/t/show_check.test 2010-07-27 14:32:42 +0000
+++ b/mysql-test/t/show_check.test 2010-11-16 19:07:35 +0000
@@ -1328,3 +1328,30 @@ disconnect con1;
# Wait till all disconnects are completed
--source include/wait_until_count_sessions.inc
+--echo #
+--echo # Bug#57306 SHOW PROCESSLIST does not display string literals well.
+--echo #
+
+SET NAMES latin1;
+SELECT GET_LOCK('t', 1000);
+--connect (con1,localhost,root,,)
+--connection con1
+SET NAMES latin1;
+--send SELECT GET_LOCK('t',1000) AS 'óóóó';
+--connection default
+# Make sure con1 has switched from "SET NAMES" to "SELECT GET_LOCK"
+let $wait_timeout= 10;
+let $wait_condition= SELECT COUNT(*) FROM INFORMATION_SCHEMA.PROCESSLIST WHERE INFO LIKE '%GET_LOCK%';
+--source include/wait_condition.inc
+--replace_column 1 ### 6 ### 7 ###
+SHOW PROCESSLIST;
+SET NAMES utf8;
+--replace_column 1 ### 6 ### 7 ###
+SHOW PROCESSLIST;
+SELECT RELEASE_LOCK('t');
+--connection con1
+--reap
+--disconnect con1
+--connection default
+SET NAMES latin1;
+
=== modified file 'sql/event_data_objects.cc'
--- a/sql/event_data_objects.cc 2010-07-27 10:25:53 +0000
+++ b/sql/event_data_objects.cc 2010-11-16 19:07:35 +0000
@@ -1526,7 +1526,7 @@ end:
thd->end_statement();
thd->cleanup_after_query();
/* Avoid races with SHOW PROCESSLIST */
- thd->set_query(NULL, 0);
+ thd->reset_query();
DBUG_PRINT("info", ("EXECUTED %s.%s ret: %d", dbname.str, name.str, ret));
=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc 2010-10-23 13:09:27 +0000
+++ b/sql/log_event.cc 2010-11-16 19:07:35 +0000
@@ -3241,7 +3241,8 @@ int Query_log_event::do_apply_event(Rela
if (is_trans_keyword() || rpl_filter->db_ok(thd->db))
{
thd->set_time((time_t)when);
- thd->set_query_and_id((char*)query_arg, q_len_arg, next_query_id());
+ thd->set_query_and_id((char*)query_arg, q_len_arg,
+ thd->charset(), next_query_id());
thd->variables.pseudo_thread_id= thread_id; // for temp tables
DBUG_PRINT("query",("%s", thd->query()));
@@ -3513,7 +3514,7 @@ end:
*/
thd->catalog= 0;
thd->set_db(NULL, 0); /* will free the current database */
- thd->set_query(NULL, 0);
+ thd->reset_query();
DBUG_PRINT("info", ("end: query= 0"));
/*
As a disk space optimization, future masters will not log an event for
@@ -4750,7 +4751,7 @@ int Load_log_event::do_apply_event(NET*
new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length);
thd->set_db(new_db.str, new_db.length);
DBUG_ASSERT(thd->query() == 0);
- thd->set_query_inner(NULL, 0); // Should not be needed
+ thd->reset_query_inner(); // Should not be needed
thd->is_slave_error= 0;
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
@@ -4944,7 +4945,7 @@ error:
const char *remember_db= thd->db;
thd->catalog= 0;
thd->set_db(NULL, 0); /* will free the current database */
- thd->set_query(NULL, 0);
+ thd->reset_query();
thd->stmt_da->can_overwrite_status= TRUE;
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
thd->stmt_da->can_overwrite_status= FALSE;
=== modified file 'sql/slave.cc'
--- a/sql/slave.cc 2010-10-16 14:20:35 +0000
+++ b/sql/slave.cc 2010-11-16 19:07:35 +0000
@@ -3011,7 +3011,7 @@ err:
sql_print_information("Slave I/O thread exiting, read up to log '%s', position %s",
IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff));
RUN_HOOK(binlog_relay_io, thread_stop, (thd, mi));
- thd->set_query(NULL, 0);
+ thd->reset_query();
thd->reset_db(NULL, 0);
if (mysql)
{
@@ -3401,7 +3401,7 @@ the slave SQL thread with \"SLAVE START\
variables is supposed to set them to 0 before terminating)).
*/
thd->catalog= 0;
- thd->set_query(NULL, 0);
+ thd->reset_query();
thd->reset_db(NULL, 0);
thd_proc_info(thd, "Waiting for slave mutex on exit");
mysql_mutex_lock(&rli->run_lock);
=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc 2010-11-11 05:06:16 +0000
+++ b/sql/sp_head.cc 2010-11-16 19:07:35 +0000
@@ -3065,8 +3065,7 @@ int sp_instr::exec_core(THD *thd, uint *
int
sp_instr_stmt::execute(THD *thd, uint *nextp)
{
- char *query;
- uint32 query_length;
+ CSET_STRING query_backup;
int res;
DBUG_ENTER("sp_instr_stmt::execute");
DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command()));
@@ -3074,8 +3073,7 @@ sp_instr_stmt::execute(THD *thd, uint *n
if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res))
DBUG_RETURN(TRUE);
- query= thd->query();
- query_length= thd->query_length();
+ query_backup.set(thd->query_string);
#if defined(ENABLED_PROFILING)
/* This s-p instr is profilable and will be captured. */
thd->profiling.set_query_source(m_query.str, m_query.length);
@@ -3105,7 +3103,7 @@ sp_instr_stmt::execute(THD *thd, uint *n
}
else
*nextp= m_ip+1;
- thd->set_query(query, query_length);
+ thd->set_query(query_backup);
thd->query_name_consts= 0;
if (!thd->is_error())
=== modified file 'sql/sql_audit.h'
--- a/sql/sql_audit.h 2010-09-20 14:17:32 +0000
+++ b/sql/sql_audit.h 2010-11-16 19:07:35 +0000
@@ -99,32 +99,29 @@ void mysql_audit_general(THD *thd, uint
{
time_t time= my_time(0);
uint msglen= msg ? strlen(msg) : 0;
- const char *query, *user;
- uint querylen, userlen;
+ const char *user;
+ uint userlen;
char user_buff[MAX_USER_HOST_SIZE];
- CHARSET_INFO *clientcs;
+ CSET_STRING query;
ha_rows rows;
if (thd)
{
- query= thd->query();
- querylen= thd->query_length();
+ query.set(thd->query_string);
user= user_buff;
userlen= make_user_name(thd, user_buff);
- clientcs= thd->variables.character_set_client;
rows= thd->warning_info->current_row_for_warning();
}
else
{
- query= user= 0;
- querylen= userlen= 0;
- clientcs= global_system_variables.character_set_client;
+ user= 0;
+ userlen= 0;
rows= 0;
}
mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, event_subtype,
error_code, time, user, userlen, msg, msglen,
- query, querylen, clientcs, rows);
+ query.str(), query.length(), query.charset(), rows);
}
#endif
}
=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc 2010-10-23 13:09:27 +0000
+++ b/sql/sql_class.cc 2010-11-16 19:07:35 +0000
@@ -2585,8 +2585,6 @@ Statement::Statement(LEX *lex_arg, MEM_R
db(NULL),
db_length(0)
{
- query_string.length= 0;
- query_string.str= NULL;
name.str= NULL;
}
@@ -2602,7 +2600,7 @@ void Statement::set_statement(Statement
id= stmt->id;
mark_used_columns= stmt->mark_used_columns;
lex= stmt->lex;
- query_string= stmt->query_string;
+ query_string.set(stmt->query_string);
}
@@ -2627,10 +2625,10 @@ void Statement::restore_backup_statement
/** Assign a new value to thd->query. */
-void Statement::set_query_inner(char *query_arg, uint32 query_length_arg)
+void Statement::set_query_inner(char *query_arg, uint32 query_length_arg,
+ CHARSET_INFO *cs_arg)
{
- query_string.str= query_arg;
- query_string.length= query_length_arg;
+ query_string.set(query_arg, query_length_arg, cs_arg);
}
@@ -3159,7 +3157,7 @@ extern "C" struct charset_info_st *thd_c
*/
extern "C" char **thd_query(MYSQL_THD thd)
{
- return(&thd->query_string.str);
+ return (&thd->query_string.string.str);
}
/**
@@ -3170,7 +3168,7 @@ extern "C" char **thd_query(MYSQL_THD th
*/
extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd)
{
- return(&thd->query_string);
+ return(&thd->query_string.string);
}
extern "C" int thd_slave_thread(const MYSQL_THD thd)
@@ -3415,20 +3413,21 @@ void THD::set_statement(Statement *stmt)
/** Assign a new value to thd->query. */
-void THD::set_query(char *query_arg, uint32 query_length_arg)
+void THD::set_query(char *query_arg, uint32 query_length_arg, CHARSET_INFO *cs)
{
mysql_mutex_lock(&LOCK_thd_data);
- set_query_inner(query_arg, query_length_arg);
+ set_query_inner(query_arg, query_length_arg, cs);
mysql_mutex_unlock(&LOCK_thd_data);
}
/** Assign a new value to thd->query and thd->query_id. */
void THD::set_query_and_id(char *query_arg, uint32 query_length_arg,
+ CHARSET_INFO *cs,
query_id_t new_query_id)
{
mysql_mutex_lock(&LOCK_thd_data);
- set_query_inner(query_arg, query_length_arg);
+ set_query_inner(query_arg, query_length_arg, cs);
query_id= new_query_id;
mysql_mutex_unlock(&LOCK_thd_data);
}
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2010-10-23 13:09:27 +0000
+++ b/sql/sql_class.h 2010-11-16 19:07:35 +0000
@@ -109,6 +109,37 @@ extern MYSQL_PLUGIN_IMPORT const char **
extern bool volatile shutdown_in_progress;
+
+/*
+ Character set armed LEX_STRING
+ with automaic initialization in constructor
+*/
+struct CSET_STRING
+{
+ LEX_STRING string;
+ CHARSET_INFO *cs;
+ CSET_STRING() { reset(); }
+ void set(char *str_arg, size_t length_arg, CHARSET_INFO *cs_arg)
+ {
+ DBUG_ASSERT(cs_arg != NULL);
+ string.str= str_arg;
+ string.length= length_arg;
+ cs= cs_arg;
+ }
+ void set(CSET_STRING &string_arg)
+ {
+ DBUG_ASSERT(string_arg.cs != NULL);
+ string= string_arg.string;
+ cs= string_arg.cs;
+ }
+ void reset() { set(NULL, 0, &my_charset_latin1);}
+
+ inline char *str() { return string.str; }
+ inline uint32 length() { return string.length; }
+ CHARSET_INFO *charset() { return cs; }
+};
+
+
#define TC_LOG_PAGE_SIZE 8192
#define TC_LOG_MIN_SIZE (3*TC_LOG_PAGE_SIZE)
@@ -722,11 +753,14 @@ public:
This printing is needed at least in SHOW PROCESSLIST and SHOW
ENGINE INNODB STATUS.
*/
- LEX_STRING query_string;
+ CSET_STRING query_string;
- inline char *query() { return query_string.str; }
- inline uint32 query_length() { return query_string.length; }
- void set_query_inner(char *query_arg, uint32 query_length_arg);
+ inline char *query() { return query_string.str(); }
+ inline uint32 query_length() { return query_string.length(); }
+ CHARSET_INFO *query_charset() { return query_string.charset(); }
+ void set_query_inner(char *query_arg, uint32 query_length_arg,
+ CHARSET_INFO *cs_arg);
+ void reset_query_inner() { set_query_inner(NULL, 0, &my_charset_latin1); }
/**
Name of the current (default) database.
@@ -2676,9 +2710,16 @@ public:
Assign a new value to thd->query and thd->query_id and mysys_var.
Protected with LOCK_thd_data mutex.
*/
- void set_query(char *query_arg, uint32 query_length_arg);
+ void set_query(char *query_arg, uint32 query_length_arg,
+ CHARSET_INFO *cs_arg);
+ void set_query(char *query_arg, uint32 query_length_arg) /*Mutex protected*/
+ { set_query(query_arg, query_length_arg, charset()); }
+ void set_query(CSET_STRING &str) /* Mutex protected */
+ { set_query(str.str(), str.length(), str.charset()); }
+ void reset_query() /* Mutex protected */
+ { set_query(NULL, 0, &my_charset_latin1); }
void set_query_and_id(char *query_arg, uint32 query_length_arg,
- query_id_t new_query_id);
+ CHARSET_INFO *cs, query_id_t new_query_id);
void set_query_id(query_id_t new_query_id);
void set_open_tables(TABLE *open_tables_arg)
{
=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc 2010-10-07 10:01:51 +0000
+++ b/sql/sql_insert.cc 2010-11-16 19:07:35 +0000
@@ -2099,7 +2099,8 @@ bool delayed_get_table(THD *thd, TABLE_L
mysql_mutex_unlock(&LOCK_thread_count);
di->thd.set_db(table_list->db, (uint) strlen(table_list->db));
di->thd.set_query(my_strdup(table_list->table_name,
- MYF(MY_WME | ME_FATALERROR)), 0);
+ MYF(MY_WME | ME_FATALERROR)),
+ 0, system_charset_info);
if (di->thd.db == NULL || di->thd.query() == NULL)
{
/* The error is reported */
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2010-10-23 13:09:27 +0000
+++ b/sql/sql_parse.cc 2010-11-16 19:07:35 +0000
@@ -550,7 +550,7 @@ static void handle_bootstrap_impl(THD *t
query= (char *) thd->memdup_w_gap(buff, length + 1,
thd->db_length + 1 +
QUERY_CACHE_FLAGS_SIZE);
- thd->set_query_and_id(query, length, next_query_id());
+ thd->set_query_and_id(query, length, thd->charset(), next_query_id());
DBUG_PRINT("query",("%-.4096s",thd->query()));
#if defined(ENABLED_PROFILING)
thd->profiling.start_new_query();
@@ -1065,7 +1065,8 @@ bool dispatch_command(enum enum_server_c
thd->security_ctx->priv_user,
(char *) thd->security_ctx->host_or_ip);
- thd->set_query_and_id(beginning_of_next_stmt, length, next_query_id());
+ thd->set_query_and_id(beginning_of_next_stmt, length,
+ thd->charset(), next_query_id());
/*
Count each statement from the client.
*/
@@ -1390,7 +1391,7 @@ bool dispatch_command(enum enum_server_c
log_slow_statement(thd);
thd_proc_info(thd, "cleaning up");
- thd->set_query(NULL, 0);
+ thd->reset_query();
thd->command=COM_SLEEP;
dec_thread_running();
thd_proc_info(thd, 0);
@@ -5494,7 +5495,8 @@ void mysql_parse(THD *thd, char *rawbuf,
if (found_semicolon && (ulong) (found_semicolon - thd->query()))
thd->set_query_inner(thd->query(),
(uint32) (found_semicolon -
- thd->query() - 1));
+ thd->query() - 1),
+ thd->charset());
/* Actually execute the query */
if (found_semicolon)
{
=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc 2010-11-03 14:15:18 +0000
+++ b/sql/sql_prepare.cc 2010-11-16 19:07:35 +0000
@@ -3735,7 +3735,8 @@ bool Prepared_statement::execute(String
to point at it even after we restore from backup. This is ok, as
expanded query was allocated in thd->mem_root.
*/
- stmt_backup.set_query_inner(thd->query(), thd->query_length());
+ stmt_backup.set_query_inner(thd->query(), thd->query_length(),
+ thd->charset());
/*
At first execution of prepared statement we may perform logical
=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc 2010-11-02 14:02:16 +0000
+++ b/sql/sql_show.cc 2010-11-16 19:07:35 +0000
@@ -1727,7 +1727,7 @@ public:
time_t start_time;
uint command;
const char *user,*host,*db,*proc_info,*state_info;
- char *query;
+ CSET_STRING query_string;
};
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
@@ -1824,12 +1824,14 @@ void mysqld_list_processes(THD *thd,cons
if (mysys_var)
mysql_mutex_unlock(&mysys_var->mutex);
- thd_info->query=0;
+ thd_info->query_string.reset();
/* Lock THD mutex that protects its data when looking at it. */
if (tmp->query())
{
uint length= min(max_query_length, tmp->query_length());
- thd_info->query= (char*) thd->strmake(tmp->query(),length);
+ char *q= thd->strmake(tmp->query(),length);
+ /* Safety: in case strmake failed, we set length to 0. */
+ thd_info->query_string.set(q, q ? length : 0, tmp->query_charset());
}
mysql_mutex_unlock(&tmp->LOCK_thd_data);
thd_info->start_time= tmp->start_time;
@@ -1857,7 +1859,8 @@ void mysqld_list_processes(THD *thd,cons
else
protocol->store_null();
protocol->store(thd_info->state_info, system_charset_info);
- protocol->store(thd_info->query, system_charset_info);
+ protocol->store(thd_info->query_string.str(),
+ thd_info->query_string.charset());
if (protocol->write())
break; /* purecov: inspected */
}
=== modified file 'storage/myisam/ha_myisam.cc'
--- a/storage/myisam/ha_myisam.cc 2010-10-06 14:34:28 +0000
+++ b/storage/myisam/ha_myisam.cc 2010-11-16 19:07:35 +0000
@@ -1499,8 +1499,7 @@ bool ha_myisam::check_and_repair(THD *th
{
int error=0;
int marked_crashed;
- char *old_query;
- uint old_query_length;
+ CSET_STRING query_backup;
HA_CHECK_OPT check_opt;
DBUG_ENTER("ha_myisam::check_and_repair");
@@ -1511,10 +1510,9 @@ bool ha_myisam::check_and_repair(THD *th
check_opt.flags|=T_QUICK;
sql_print_warning("Checking table: '%s'",table->s->path.str);
- old_query= thd->query();
- old_query_length= thd->query_length();
+ query_backup.set(thd->query_string);
thd->set_query(table->s->table_name.str,
- (uint) table->s->table_name.length);
+ (uint) table->s->table_name.length, system_charset_info);
if ((marked_crashed= mi_is_crashed(file)) || check(thd, &check_opt))
{
@@ -1527,7 +1525,7 @@ bool ha_myisam::check_and_repair(THD *th
if (repair(thd, &check_opt))
error=1;
}
- thd->set_query(old_query, old_query_length);
+ thd->set_query(query_backup);
DBUG_RETURN(error);
}
Attachment: [text/bzr-bundle] bzr/bar@mysql.com-20101116190735-wx03bvp0fue4m89a.bundle
| Thread |
|---|
| • bzr commit into mysql-5.5-bugteam branch (bar:3121) Bug#57306 | Alexander Barkov | 16 Nov |