diff options
-rw-r--r-- | mysql-test/r/show_check.result | 24 | ||||
-rw-r--r-- | mysql-test/t/show_check.test | 27 | ||||
-rw-r--r-- | sql/event_data_objects.cc | 2 | ||||
-rw-r--r-- | sql/log_event.cc | 21 | ||||
-rw-r--r-- | sql/slave.cc | 4 | ||||
-rw-r--r-- | sql/sp_head.cc | 7 | ||||
-rw-r--r-- | sql/sql_audit.h | 17 | ||||
-rw-r--r-- | sql/sql_class.cc | 22 | ||||
-rw-r--r-- | sql/sql_class.h | 72 | ||||
-rw-r--r-- | sql/sql_insert.cc | 3 | ||||
-rw-r--r-- | sql/sql_parse.cc | 10 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 2 | ||||
-rw-r--r-- | sql/sql_show.cc | 11 | ||||
-rw-r--r-- | storage/myisam/ha_myisam.cc | 9 |
14 files changed, 170 insertions, 61 deletions
diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index c1a75281e0e..d2be40834b6 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -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; diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index fa9dc7472fe..060022dee64 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -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%' AND ID != CONNECTION_ID(); +--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; + diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 52c509621ac..890d8be3be4 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -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)); diff --git a/sql/log_event.cc b/sql/log_event.cc index b4641af07c5..d3aa94fcce2 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -3241,7 +3241,8 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli, 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())); @@ -3293,6 +3294,18 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli, goto compare_errors; } thd->update_charset(); // for the charset change to take effect + /* + Reset thd->query_string.cs to the newly set value. + Note, there is a small flaw here. For a very short time frame + if the new charset is different from the old charset and + if another thread executes "SHOW PROCESSLIST" after + the above thd->set_query_and_id() and before this thd->set_query(), + and if the current query has some non-ASCII characters, + the another thread may see some '?' marks in the PROCESSLIST + result. This should be acceptable now. This is a reminder + to fix this if any refactoring happens here sometime. + */ + thd->set_query((char*) query_arg, q_len_arg, thd->charset()); } } if (time_zone_len) @@ -3513,7 +3526,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 +4763,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli, 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 +4957,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; diff --git a/sql/slave.cc b/sql/slave.cc index 9978b4cb0e2..8874eb7f9bb 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -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\". We stopped at log \ 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); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 01c11d35a62..de20b9547c5 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3065,8 +3065,6 @@ int sp_instr::exec_core(THD *thd, uint *nextp) int sp_instr_stmt::execute(THD *thd, uint *nextp) { - char *query; - uint32 query_length; int res; DBUG_ENTER("sp_instr_stmt::execute"); DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command())); @@ -3074,8 +3072,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res)) DBUG_RETURN(TRUE); - query= thd->query(); - query_length= thd->query_length(); + const CSET_STRING query_backup= 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 +3102,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) } 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()) diff --git a/sql/sql_audit.h b/sql/sql_audit.h index 7cb40181bd7..cbc4c7a7232 100644 --- a/sql/sql_audit.h +++ b/sql/sql_audit.h @@ -99,32 +99,29 @@ void mysql_audit_general(THD *thd, uint event_subtype, { 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= 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 } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index fc4ab1bd27b..bace0295a04 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2586,8 +2586,6 @@ Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg, db(NULL), db_length(0) { - query_string.length= 0; - query_string.str= NULL; name.str= NULL; } @@ -2626,15 +2624,6 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup) } -/** Assign a new value to thd->query. */ - -void Statement::set_query_inner(char *query_arg, uint32 query_length_arg) -{ - query_string.str= query_arg; - query_string.length= query_length_arg; -} - - void THD::end_statement() { /* Cleanup SQL processing state to reuse this statement in next query. */ @@ -3167,7 +3156,7 @@ extern "C" struct charset_info_st *thd_charset(MYSQL_THD thd) */ extern "C" char **thd_query(MYSQL_THD thd) { - return(&thd->query_string.str); + return (&thd->query_string.string.str); } /** @@ -3178,7 +3167,7 @@ extern "C" char **thd_query(MYSQL_THD thd) */ 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) @@ -3423,20 +3412,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(const CSET_STRING &string_arg) { mysql_mutex_lock(&LOCK_thd_data); - set_query_inner(query_arg, query_length_arg); + set_query_inner(string_arg); 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); } diff --git a/sql/sql_class.h b/sql/sql_class.h index 24fd765d653..0399ddbca57 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -109,6 +109,41 @@ extern MYSQL_PLUGIN_IMPORT const char **errmesg; extern bool volatile shutdown_in_progress; +extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd); +extern "C" char **thd_query(MYSQL_THD thd); + +/** + @class CSET_STRING + @brief Character set armed LEX_STRING +*/ +class CSET_STRING +{ +private: + LEX_STRING string; + CHARSET_INFO *cs; +public: + CSET_STRING() : cs(&my_charset_bin) + { + string.str= NULL; + string.length= 0; + } + CSET_STRING(char *str_arg, size_t length_arg, CHARSET_INFO *cs_arg) : + cs(cs_arg) + { + DBUG_ASSERT(cs_arg != NULL); + string.str= str_arg; + string.length= length_arg; + } + + inline char *str() const { return string.str; } + inline uint32 length() const { return string.length; } + CHARSET_INFO *charset() const { return cs; } + + friend LEX_STRING * thd_query_string (MYSQL_THD thd); + friend char **thd_query(MYSQL_THD thd); +}; + + #define TC_LOG_PAGE_SIZE 8192 #define TC_LOG_MIN_SIZE (3*TC_LOG_PAGE_SIZE) @@ -722,12 +757,24 @@ public: This printing is needed at least in SHOW PROCESSLIST and SHOW ENGINE INNODB STATUS. */ - LEX_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); + CSET_STRING query_string; + inline char *query() const { return query_string.str(); } + inline uint32 query_length() const { return query_string.length(); } + CHARSET_INFO *query_charset() const { return query_string.charset(); } + void set_query_inner(const CSET_STRING &string_arg) + { + query_string= string_arg; + } + void set_query_inner(char *query_arg, uint32 query_length_arg, + CHARSET_INFO *cs_arg) + { + set_query_inner(CSET_STRING(query_arg, query_length_arg, cs_arg)); + } + void reset_query_inner() + { + set_query_inner(CSET_STRING()); + } /** Name of the current (default) database. @@ -2676,9 +2723,20 @@ 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) + { + set_query(CSET_STRING(query_arg, query_length_arg, cs_arg)); + } + void set_query(char *query_arg, uint32 query_length_arg) /*Mutex protected*/ + { + set_query(CSET_STRING(query_arg, query_length_arg, charset())); + } + void set_query(const CSET_STRING &str); /* Mutex protected */ + void reset_query() /* Mutex protected */ + { set_query(CSET_STRING()); } 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) { diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index a81c9ae15a0..eaaf7901e25 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2099,7 +2099,8 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) 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 */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index daf8971fb68..4d2c0cbb60a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -550,7 +550,7 @@ static void handle_bootstrap_impl(THD *thd) 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_command command, THD *thd, 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_command command, THD *thd, 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, uint length, 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) { diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 851782c623f..664d08f27a9 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -3742,7 +3742,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) 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_string); /* At first execution of prepared statement we may perform logical diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 19a73c85a9b..6283deed331 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -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,const char *user, bool verbose) if (mysys_var) mysql_mutex_unlock(&mysys_var->mutex); - thd_info->query=0; /* 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= + CSET_STRING(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,const char *user, bool verbose) 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 */ } diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index 2ba62d03c6b..c4bb6d7dbd4 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -1499,8 +1499,6 @@ bool ha_myisam::check_and_repair(THD *thd) { int error=0; int marked_crashed; - char *old_query; - uint old_query_length; HA_CHECK_OPT check_opt; DBUG_ENTER("ha_myisam::check_and_repair"); @@ -1511,10 +1509,9 @@ bool ha_myisam::check_and_repair(THD *thd) 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(); + const CSET_STRING query_backup= 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 +1524,7 @@ bool ha_myisam::check_and_repair(THD *thd) if (repair(thd, &check_opt)) error=1; } - thd->set_query(old_query, old_query_length); + thd->set_query(query_backup); DBUG_RETURN(error); } |