Commit 91599701 authored by Sergei Golubchik's avatar Sergei Golubchik

Bug#29363867: LOST CONNECTION TO MYSQL SERVER DURING QUERY

plugin variables in SET  only locked the plugin till the end of the
statement. If SET with a plugin variable was prepared, it was possible
to uninstall the plugin before EXECUTE. Then EXECUTE would crash,
trying to resolve a now-invalid pointer to a disappeared variable.

Fix: keep plugins locked until the prepared statement is closed.
parent 4f63b6cf
...@@ -30,3 +30,38 @@ disconnect con2; ...@@ -30,3 +30,38 @@ disconnect con2;
USE test; USE test;
DROP PROCEDURE p_install; DROP PROCEDURE p_install;
DROP PROCEDURE p_show_vars; DROP PROCEDURE p_show_vars;
#
# Bug#29363867: LOST CONNECTION TO MYSQL SERVER DURING QUERY
#
## prepared SET with a plugin variable prevents uninstall
install plugin query_response_time soname 'query_response_time';
prepare s from 'set global query_response_time_range_base=16';
select plugin_status from information_schema.plugins where plugin_name='query_response_time';
plugin_status
ACTIVE
uninstall plugin query_response_time;
Warnings:
Warning 1620 Plugin is busy and will be uninstalled on shutdown
execute s;
execute s;
select plugin_status from information_schema.plugins where plugin_name='query_response_time';
plugin_status
DELETED
deallocate prepare s;
select plugin_status from information_schema.plugins where plugin_name='query_response_time';
plugin_status
## prepared SET mentioning a plugin otherwise does not prevent uninstall
install plugin archive soname 'ha_archive';
create table t1 (a int) engine=archive;
insert t1 values (1),(2),(3);
prepare s from 'set session auto_increment_increment=(select count(*) from t1)';
flush tables;
select plugin_status from information_schema.plugins where plugin_name='archive';
plugin_status
ACTIVE
uninstall plugin archive;
select plugin_status from information_schema.plugins where plugin_name='archive';
plugin_status
execute s;
ERROR 42000: Unknown storage engine 'ARCHIVE'
drop table t1;
if (!$QUERY_RESPONSE_TIME_SO) {
skip Needs query_response_time loadable plugin;
}
if (!$HA_ARCHIVE_SO) {
skip Needs Archive loadable plugin;
}
--echo # --echo #
--echo # MDEV-5345 - Deadlock between mysql_change_user(), SHOW VARIABLES and --echo # MDEV-5345 - Deadlock between mysql_change_user(), SHOW VARIABLES and
--echo # INSTALL PLUGIN --echo # INSTALL PLUGIN
...@@ -54,3 +61,31 @@ disconnect con2; ...@@ -54,3 +61,31 @@ disconnect con2;
USE test; USE test;
DROP PROCEDURE p_install; DROP PROCEDURE p_install;
DROP PROCEDURE p_show_vars; DROP PROCEDURE p_show_vars;
--echo #
--echo # Bug#29363867: LOST CONNECTION TO MYSQL SERVER DURING QUERY
--echo #
--echo ## prepared SET with a plugin variable prevents uninstall
install plugin query_response_time soname 'query_response_time';
prepare s from 'set global query_response_time_range_base=16';
select plugin_status from information_schema.plugins where plugin_name='query_response_time';
uninstall plugin query_response_time;
execute s;
execute s;
select plugin_status from information_schema.plugins where plugin_name='query_response_time';
deallocate prepare s;
select plugin_status from information_schema.plugins where plugin_name='query_response_time';
--echo ## prepared SET mentioning a plugin otherwise does not prevent uninstall
install plugin archive soname 'ha_archive';
create table t1 (a int) engine=archive;
insert t1 values (1),(2),(3);
prepare s from 'set session auto_increment_increment=(select count(*) from t1)';
flush tables;
select plugin_status from information_schema.plugins where plugin_name='archive';
uninstall plugin archive;
select plugin_status from information_schema.plugins where plugin_name='archive';
--error ER_UNKNOWN_STORAGE_ENGINE
execute s;
drop table t1;
...@@ -765,15 +765,15 @@ void lex_end(LEX *lex) ...@@ -765,15 +765,15 @@ void lex_end(LEX *lex)
DBUG_ENTER("lex_end"); DBUG_ENTER("lex_end");
DBUG_PRINT("enter", ("lex: %p", lex)); DBUG_PRINT("enter", ("lex: %p", lex));
lex_end_stage1(lex); lex_unlock_plugins(lex);
lex_end_stage2(lex); lex_end_nops(lex);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
void lex_end_stage1(LEX *lex) void lex_unlock_plugins(LEX *lex)
{ {
DBUG_ENTER("lex_end_stage1"); DBUG_ENTER("lex_unlock_plugins");
/* release used plugins */ /* release used plugins */
if (lex->plugins.elements) /* No function call and no mutex if no plugins. */ if (lex->plugins.elements) /* No function call and no mutex if no plugins. */
...@@ -782,33 +782,23 @@ void lex_end_stage1(LEX *lex) ...@@ -782,33 +782,23 @@ void lex_end_stage1(LEX *lex)
lex->plugins.elements); lex->plugins.elements);
} }
reset_dynamic(&lex->plugins); reset_dynamic(&lex->plugins);
if (lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE)
{
/*
Don't delete lex->sphead, it'll be needed for EXECUTE.
Note that of all statements that populate lex->sphead
only SQLCOM_COMPOUND can be PREPAREd
*/
DBUG_ASSERT(lex->sphead == 0 || lex->sql_command == SQLCOM_COMPOUND);
}
else
{
sp_head::destroy(lex->sphead);
lex->sphead= NULL;
}
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
/* /*
Don't delete lex->sphead, it'll be needed for EXECUTE.
Note that of all statements that populate lex->sphead
only SQLCOM_COMPOUND can be PREPAREd
MASTER INFO parameters (or state) is normally cleared towards the end MASTER INFO parameters (or state) is normally cleared towards the end
of a statement. But in case of PS, the state needs to be preserved during of a statement. But in case of PS, the state needs to be preserved during
its lifetime and should only be cleared on PS close or deallocation. its lifetime and should only be cleared on PS close or deallocation.
*/ */
void lex_end_stage2(LEX *lex) void lex_end_nops(LEX *lex)
{ {
DBUG_ENTER("lex_end_stage2"); DBUG_ENTER("lex_end_nops");
sp_head::destroy(lex->sphead);
lex->sphead= NULL;
/* Reset LEX_MASTER_INFO */ /* Reset LEX_MASTER_INFO */
lex->mi.reset(lex->sql_command == SQLCOM_CHANGE_MASTER); lex->mi.reset(lex->sql_command == SQLCOM_CHANGE_MASTER);
......
...@@ -3412,8 +3412,8 @@ extern void lex_init(void); ...@@ -3412,8 +3412,8 @@ extern void lex_init(void);
extern void lex_free(void); extern void lex_free(void);
extern void lex_start(THD *thd); extern void lex_start(THD *thd);
extern void lex_end(LEX *lex); extern void lex_end(LEX *lex);
extern void lex_end_stage1(LEX *lex); extern void lex_end_nops(LEX *lex);
extern void lex_end_stage2(LEX *lex); extern void lex_unlock_plugins(LEX *lex);
void end_lex_with_single_table(THD *thd, TABLE *table, LEX *old_lex); void end_lex_with_single_table(THD *thd, TABLE *table, LEX *old_lex);
int init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex); int init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex);
extern int MYSQLlex(union YYSTYPE *yylval, THD *thd); extern int MYSQLlex(union YYSTYPE *yylval, THD *thd);
......
...@@ -4307,8 +4307,10 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) ...@@ -4307,8 +4307,10 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
thd->release_transactional_locks(); thd->release_transactional_locks();
} }
/* Preserve CHANGE MASTER attributes */ /* Preserve locked plugins for SET */
lex_end_stage1(lex); if (lex->sql_command != SQLCOM_SET_OPTION)
lex_unlock_plugins(lex);
cleanup_stmt(); cleanup_stmt();
thd->restore_backup_statement(this, &stmt_backup); thd->restore_backup_statement(this, &stmt_backup);
thd->stmt_arena= old_stmt_arena; thd->stmt_arena= old_stmt_arena;
...@@ -5189,7 +5191,7 @@ void Prepared_statement::deallocate_immediate() ...@@ -5189,7 +5191,7 @@ void Prepared_statement::deallocate_immediate()
status_var_increment(thd->status_var.com_stmt_close); status_var_increment(thd->status_var.com_stmt_close);
/* It should now be safe to reset CHANGE MASTER parameters */ /* It should now be safe to reset CHANGE MASTER parameters */
lex_end_stage2(lex); lex_end(lex);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment