Bug#16581: deadlock: server and client both read from connection in

           'conc_sys' test

Concurrent execution of SELECT involing at least two INFORMATION_SCHEMA
tables, DROP DATABASE statement and DROP TABLE statement could have
resulted in stalled connection for this SELECT statement.

The problem was that for the first query of a join there was a race
between select from I_S.TABLES and DROP DATABASE, and the error (no
such database) was prepared to be send to the client, but the join
processing was continued.  On second query to I_S.COLUMNS there was a
race with DROP TABLE, but this error (no such table) was downgraded to
warning, and thd->net.report_error was reset.  And so neither result
nor error was sent to the client.

The solution is to stop join processing once it is clear we are going
to report a error, and also to downgrade to warnings file system errors
like 'no such database' (unless we are in the 'SHOW' command), because
I_S is designed not to use locks and the query to I_S should not abort
if something is dropped in the middle.

No test case is provided since this bug is a result of a race, and is
timing dependant.  But we test that plain SHOW TABLES and SHOW COLUMNS
give a error if there is no such database or a table respectively.
parent ed2d8cce
...@@ -625,3 +625,7 @@ View Create View ...@@ -625,3 +625,7 @@ View Create View
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select sql_cache 1 AS `1` v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select sql_cache 1 AS `1`
DROP PROCEDURE p1; DROP PROCEDURE p1;
DROP VIEW v1; DROP VIEW v1;
SHOW TABLES FROM no_such_database;
ERROR 42000: Unknown database 'no_such_database'
SHOW COLUMNS FROM no_such_table;
ERROR 42S02: Table 'test.no_such_table' doesn't exist
...@@ -495,4 +495,15 @@ SHOW CREATE VIEW v1; ...@@ -495,4 +495,15 @@ SHOW CREATE VIEW v1;
DROP PROCEDURE p1; DROP PROCEDURE p1;
DROP VIEW v1; DROP VIEW v1;
#
# Check that SHOW TABLES and SHOW COLUMNS give a error if there is no
# referenced database and table respectively.
#
--error ER_BAD_DB_ERROR
SHOW TABLES FROM no_such_database;
--error ER_NO_SUCH_TABLE
SHOW COLUMNS FROM no_such_table;
# End of 5.0 tests. # End of 5.0 tests.
...@@ -865,8 +865,6 @@ bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create); ...@@ -865,8 +865,6 @@ bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create);
void mysqld_list_processes(THD *thd,const char *user,bool verbose); void mysqld_list_processes(THD *thd,const char *user,bool verbose);
int mysqld_show_status(THD *thd); int mysqld_show_status(THD *thd);
int mysqld_show_variables(THD *thd,const char *wild); int mysqld_show_variables(THD *thd,const char *wild);
int mysql_find_files(THD *thd,List<char> *files, const char *db,
const char *path, const char *wild, bool dir);
bool mysqld_show_storage_engines(THD *thd); bool mysqld_show_storage_engines(THD *thd);
bool mysqld_show_privileges(THD *thd); bool mysqld_show_privileges(THD *thd);
bool mysqld_show_column_types(THD *thd); bool mysqld_show_column_types(THD *thd);
......
...@@ -250,9 +250,35 @@ bool mysqld_show_column_types(THD *thd) ...@@ -250,9 +250,35 @@ bool mysqld_show_column_types(THD *thd)
} }
int /*
mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, find_files() - find files in a given directory.
const char *wild, bool dir)
SYNOPSIS
find_files()
thd thread handler
files put found files in this list
db database name to set in TABLE_LIST structure
path path to database
wild filter for found files
dir read databases in path if TRUE, read .frm files in
database otherwise
RETURN
FIND_FILES_OK success
FIND_FILES_OOM out of memory error
FIND_FILES_DIR no such directory, or directory can't be read
*/
enum find_files_result {
FIND_FILES_OK,
FIND_FILES_OOM,
FIND_FILES_DIR
};
static
find_files_result
find_files(THD *thd, List<char> *files, const char *db,
const char *path, const char *wild, bool dir)
{ {
uint i; uint i;
char *ext; char *ext;
...@@ -262,7 +288,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, ...@@ -262,7 +288,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
uint col_access=thd->col_access; uint col_access=thd->col_access;
#endif #endif
TABLE_LIST table_list; TABLE_LIST table_list;
DBUG_ENTER("mysql_find_files"); DBUG_ENTER("find_files");
if (wild && !wild[0]) if (wild && !wild[0])
wild=0; wild=0;
...@@ -275,7 +301,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, ...@@ -275,7 +301,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db); my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db);
else else
my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno); my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
DBUG_RETURN(-1); DBUG_RETURN(FIND_FILES_DIR);
} }
for (i=0 ; i < (uint) dirp->number_off_files ; i++) for (i=0 ; i < (uint) dirp->number_off_files ; i++)
...@@ -337,7 +363,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, ...@@ -337,7 +363,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
if (files->push_back(thd->strdup(file->name))) if (files->push_back(thd->strdup(file->name)))
{ {
my_dirend(dirp); my_dirend(dirp);
DBUG_RETURN(-1); DBUG_RETURN(FIND_FILES_OOM);
} }
} }
DBUG_PRINT("info",("found: %d files", files->elements)); DBUG_PRINT("info",("found: %d files", files->elements));
...@@ -345,7 +371,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path, ...@@ -345,7 +371,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
VOID(ha_find_files(thd,db,path,wild,dir,files)); VOID(ha_find_files(thd,db,path,wild,dir,files));
DBUG_RETURN(0); DBUG_RETURN(FIND_FILES_OK);
} }
...@@ -2000,8 +2026,8 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table) ...@@ -2000,8 +2026,8 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table)
wild string otherwise it's db name; wild string otherwise it's db name;
RETURN RETURN
1 error zero success
0 success non-zero error
*/ */
int make_db_list(THD *thd, List<char> *files, int make_db_list(THD *thd, List<char> *files,
...@@ -2027,8 +2053,8 @@ int make_db_list(THD *thd, List<char> *files, ...@@ -2027,8 +2053,8 @@ int make_db_list(THD *thd, List<char> *files,
if (files->push_back(thd->strdup(information_schema_name.str))) if (files->push_back(thd->strdup(information_schema_name.str)))
return 1; return 1;
} }
return mysql_find_files(thd, files, NullS, mysql_data_home, return (find_files(thd, files, NullS, mysql_data_home,
idx_field_vals->db_value, 1); idx_field_vals->db_value, 1) != FIND_FILES_OK);
} }
/* /*
...@@ -2055,7 +2081,8 @@ int make_db_list(THD *thd, List<char> *files, ...@@ -2055,7 +2081,8 @@ int make_db_list(THD *thd, List<char> *files,
if (files->push_back(thd->strdup(information_schema_name.str))) if (files->push_back(thd->strdup(information_schema_name.str)))
return 1; return 1;
*with_i_schema= 1; *with_i_schema= 1;
return mysql_find_files(thd, files, NullS, mysql_data_home, NullS, 1); return (find_files(thd, files, NullS,
mysql_data_home, NullS, 1) != FIND_FILES_OK);
} }
...@@ -2204,9 +2231,28 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) ...@@ -2204,9 +2231,28 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
strxmov(path, mysql_data_home, "/", base_name, NullS); strxmov(path, mysql_data_home, "/", base_name, NullS);
end= path + (len= unpack_dirname(path,path)); end= path + (len= unpack_dirname(path,path));
len= FN_LEN - len; len= FN_LEN - len;
if (mysql_find_files(thd, &files, base_name, find_files_result res= find_files(thd, &files, base_name,
path, idx_field_vals.table_value, 0)) path, idx_field_vals.table_value, 0);
goto err; if (res != FIND_FILES_OK)
{
/*
Downgrade errors about problems with database directory to
warnings if this is not a 'SHOW' command. Another thread
may have dropped database, and we may still have a name
for that directory.
*/
if (res == FIND_FILES_DIR && lex->orig_sql_command == SQLCOM_END)
{
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
thd->net.last_errno, thd->net.last_error);
thd->clear_error();
continue;
}
else
{
goto err;
}
}
if (lower_case_table_names) if (lower_case_table_names)
orig_base_name= thd->strdup(base_name); orig_base_name= thd->strdup(base_name);
} }
...@@ -3969,7 +4015,12 @@ bool get_schema_tables_result(JOIN *join) ...@@ -3969,7 +4015,12 @@ bool get_schema_tables_result(JOIN *join)
if (table_list->schema_table->fill_table(thd, table_list, if (table_list->schema_table->fill_table(thd, table_list,
tab->select_cond)) tab->select_cond))
{
result= 1; result= 1;
join->error= 1;
table_list->is_schema_table_processed= TRUE;
break;
}
table_list->is_schema_table_processed= TRUE; table_list->is_schema_table_processed= TRUE;
} }
} }
......
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