Fix for Bug#13001491: MYSQL_REFRESH CRASHES WHEN STORED ROUTINES ARE RUN CONCURRENTLY.
The main problem was that lex_start() was forgotten to be called before processing COM_REFRESH. Another problem discovered was that if failures to flush the error log were not properly handled, which resulted in the server crash. The user-visible effect of these problems were: - if COM_REFRESH command was sent after SQL-queries of some sort, the server would crash. - if COM_REFRESH was requested with REFRESH_LOG only, and the error log failed to flush, the server would crash. The error log fails to flush when it points to unavailable file (for example, due to restricted permissions). The fixes are: - call lex_start() in the beginning of COM_REFRESH; - handle failures to flush the error log properly, i.e. raise ER_UNKNOWN_ERROR.
This commit is contained in:
parent
a2cd617a4b
commit
41dc304928
@ -1491,6 +1491,14 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||||||
case COM_REFRESH:
|
case COM_REFRESH:
|
||||||
{
|
{
|
||||||
int not_used;
|
int not_used;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initialize thd->lex since it's used in many base functions, such as
|
||||||
|
open_tables(). Otherwise, it remains unitialized and may cause crash
|
||||||
|
during execution of COM_REFRESH.
|
||||||
|
*/
|
||||||
|
lex_start(thd);
|
||||||
|
|
||||||
status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
|
status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
|
||||||
ulong options= (ulong) (uchar) packet[0];
|
ulong options= (ulong) (uchar) packet[0];
|
||||||
if (check_global_access(thd,RELOAD_ACL))
|
if (check_global_access(thd,RELOAD_ACL))
|
||||||
@ -6986,7 +6994,14 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
|
|||||||
if (ha_flush_logs(NULL))
|
if (ha_flush_logs(NULL))
|
||||||
result=1;
|
result=1;
|
||||||
if (flush_error_log())
|
if (flush_error_log())
|
||||||
result=1;
|
{
|
||||||
|
/*
|
||||||
|
When flush_error_log() failed, my_error() has not been called.
|
||||||
|
So, we have to do it here to keep the protocol.
|
||||||
|
*/
|
||||||
|
my_error(ER_UNKNOWN_ERROR, MYF(0));
|
||||||
|
result= 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#ifdef HAVE_QUERY_CACHE
|
#ifdef HAVE_QUERY_CACHE
|
||||||
if (options & REFRESH_QUERY_CACHE_FREE)
|
if (options & REFRESH_QUERY_CACHE_FREE)
|
||||||
@ -7035,7 +7050,13 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
|
|||||||
return 1; // Killed
|
return 1; // Killed
|
||||||
if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
|
if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
|
||||||
FALSE : TRUE, TRUE))
|
FALSE : TRUE, TRUE))
|
||||||
result= 1;
|
{
|
||||||
|
/*
|
||||||
|
NOTE: my_error() has been already called by reopen_tables() within
|
||||||
|
close_cached_tables().
|
||||||
|
*/
|
||||||
|
result= 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (make_global_read_lock_block_commit(thd)) // Killed
|
if (make_global_read_lock_block_commit(thd)) // Killed
|
||||||
{
|
{
|
||||||
@ -7048,7 +7069,13 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
|
|||||||
{
|
{
|
||||||
if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
|
if (close_cached_tables(thd, tables, FALSE, (options & REFRESH_FAST) ?
|
||||||
FALSE : TRUE, FALSE))
|
FALSE : TRUE, FALSE))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
NOTE: my_error() has been already called by reopen_tables() within
|
||||||
|
close_cached_tables().
|
||||||
|
*/
|
||||||
result= 1;
|
result= 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
my_dbopt_cleanup();
|
my_dbopt_cleanup();
|
||||||
}
|
}
|
||||||
@ -7065,7 +7092,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
|
|||||||
tmp_write_to_binlog= 0;
|
tmp_write_to_binlog= 0;
|
||||||
if (reset_master(thd))
|
if (reset_master(thd))
|
||||||
{
|
{
|
||||||
result=1;
|
/* NOTE: my_error() has been already called by reset_master(). */
|
||||||
|
result= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -7073,7 +7101,10 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
|
|||||||
if (options & REFRESH_DES_KEY_FILE)
|
if (options & REFRESH_DES_KEY_FILE)
|
||||||
{
|
{
|
||||||
if (des_key_file && load_des_key_file(des_key_file))
|
if (des_key_file && load_des_key_file(des_key_file))
|
||||||
result= 1;
|
{
|
||||||
|
/* NOTE: my_error() has been already called by load_des_key_file(). */
|
||||||
|
result= 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_REPLICATION
|
#ifdef HAVE_REPLICATION
|
||||||
@ -7082,7 +7113,10 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
|
|||||||
tmp_write_to_binlog= 0;
|
tmp_write_to_binlog= 0;
|
||||||
pthread_mutex_lock(&LOCK_active_mi);
|
pthread_mutex_lock(&LOCK_active_mi);
|
||||||
if (reset_slave(thd, active_mi))
|
if (reset_slave(thd, active_mi))
|
||||||
result=1;
|
{
|
||||||
|
/* NOTE: my_error() has been already called by reset_slave(). */
|
||||||
|
result= 1;
|
||||||
|
}
|
||||||
pthread_mutex_unlock(&LOCK_active_mi);
|
pthread_mutex_unlock(&LOCK_active_mi);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -18513,6 +18513,85 @@ static void test_bug56976()
|
|||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Bug#13001491: MYSQL_REFRESH CRASHES WHEN STORED ROUTINES ARE RUN CONCURRENTLY.
|
||||||
|
*/
|
||||||
|
static void test_bug13001491()
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
char query[MAX_TEST_QUERY_LENGTH];
|
||||||
|
MYSQL *c;
|
||||||
|
|
||||||
|
myheader("test_bug13001491");
|
||||||
|
|
||||||
|
snprintf(query, MAX_TEST_QUERY_LENGTH,
|
||||||
|
"GRANT ALL PRIVILEGES ON *.* TO mysqltest_u1@%s",
|
||||||
|
opt_host ? opt_host : "'localhost'");
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, query);
|
||||||
|
myquery(rc);
|
||||||
|
|
||||||
|
snprintf(query, MAX_TEST_QUERY_LENGTH,
|
||||||
|
"GRANT RELOAD ON *.* TO mysqltest_u1@%s",
|
||||||
|
opt_host ? opt_host : "'localhost'");
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, query);
|
||||||
|
myquery(rc);
|
||||||
|
|
||||||
|
c= mysql_client_init(NULL);
|
||||||
|
|
||||||
|
DIE_UNLESS(mysql_real_connect(c, opt_host, "mysqltest_u1", NULL,
|
||||||
|
current_db, opt_port, opt_unix_socket,
|
||||||
|
CLIENT_MULTI_STATEMENTS |
|
||||||
|
CLIENT_MULTI_RESULTS));
|
||||||
|
|
||||||
|
rc= mysql_query(c, "DROP PROCEDURE IF EXISTS p1");
|
||||||
|
myquery(rc);
|
||||||
|
|
||||||
|
rc= mysql_query(c,
|
||||||
|
"CREATE PROCEDURE p1() "
|
||||||
|
"BEGIN "
|
||||||
|
" DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; "
|
||||||
|
" SELECT COUNT(*) "
|
||||||
|
" FROM INFORMATION_SCHEMA.PROCESSLIST "
|
||||||
|
" GROUP BY user "
|
||||||
|
" ORDER BY NULL "
|
||||||
|
" INTO @a; "
|
||||||
|
"END");
|
||||||
|
myquery(rc);
|
||||||
|
|
||||||
|
rc= mysql_query(c, "CALL p1()");
|
||||||
|
myquery(rc);
|
||||||
|
|
||||||
|
mysql_free_result(mysql_store_result(c));
|
||||||
|
|
||||||
|
/* Check that mysql_refresh() succeeds without REFRESH_LOG. */
|
||||||
|
rc= mysql_refresh(c, REFRESH_GRANT |
|
||||||
|
REFRESH_TABLES | REFRESH_HOSTS |
|
||||||
|
REFRESH_STATUS | REFRESH_THREADS);
|
||||||
|
myquery(rc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check that mysql_refresh(REFRESH_LOG) does not crash the server even if it
|
||||||
|
fails. mysql_refresh(REFRESH_LOG) fails when error log points to unavailable
|
||||||
|
location.
|
||||||
|
*/
|
||||||
|
mysql_refresh(c, REFRESH_LOG);
|
||||||
|
|
||||||
|
rc= mysql_query(c, "DROP PROCEDURE p1");
|
||||||
|
myquery(rc);
|
||||||
|
|
||||||
|
mysql_close(c);
|
||||||
|
c= NULL;
|
||||||
|
|
||||||
|
snprintf(query, MAX_TEST_QUERY_LENGTH,
|
||||||
|
"DROP USER mysqltest_u1@%s",
|
||||||
|
opt_host ? opt_host : "'localhost'");
|
||||||
|
|
||||||
|
rc= mysql_query(mysql, query);
|
||||||
|
myquery(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Read and parse arguments and MySQL options from my.cnf
|
Read and parse arguments and MySQL options from my.cnf
|
||||||
@ -18842,6 +18921,7 @@ static struct my_tests_st my_tests[]= {
|
|||||||
{ "test_bug47485", test_bug47485 },
|
{ "test_bug47485", test_bug47485 },
|
||||||
{ "test_bug58036", test_bug58036 },
|
{ "test_bug58036", test_bug58036 },
|
||||||
{ "test_bug56976", test_bug56976 },
|
{ "test_bug56976", test_bug56976 },
|
||||||
|
{ "test_bug13001491", test_bug13001491 },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user