Commit 68a6705e authored by Sergei Golubchik's avatar Sergei Golubchik

MDEV-4441 DROP DATABASE with a newly created ARCHIVE table does not work

1. DROP DATABASE should use ha_discover_table_names(), not look at .frm files.
2. filename_to_tablename() also encodes temp file names #sql- -> #mysql50##sql
3. no special treatment for #sql- files, no TABLE_LIST::internal_tmp_table
4. discover also table file names, that start from #
parent 6a0a4f00
...@@ -123,3 +123,6 @@ select * from `a/../`; ...@@ -123,3 +123,6 @@ select * from `a/../`;
a a
flush tables; flush tables;
drop table `a/../`; drop table `a/../`;
create database test1;
create table test1.t1 (i int) engine=archive;
drop database test1;
...@@ -102,3 +102,10 @@ select * from `a/../`; flush tables; ...@@ -102,3 +102,10 @@ select * from `a/../`; flush tables;
remove_file $mysqld_datadir/test/a@002f@002e@002e@002f.frm; remove_file $mysqld_datadir/test/a@002f@002e@002e@002f.frm;
drop table `a/../`; drop table `a/../`;
#
# MDEV-4441 DROP DATABASE with a newly created ARCHIVE table does not work
#
create database test1;
create table test1.t1 (i int) engine=archive;
drop database test1;
...@@ -8,12 +8,12 @@ select * from mysqltest1.t1 into outfile 'mysqltest1/f1.txt'; ...@@ -8,12 +8,12 @@ select * from mysqltest1.t1 into outfile 'mysqltest1/f1.txt';
create table mysqltest1.t2 (n int); create table mysqltest1.t2 (n int);
create table mysqltest1.t3 (n int); create table mysqltest1.t3 (n int);
drop database mysqltest1; drop database mysqltest1;
ERROR HY000: Error dropping database (can't rmdir './mysqltest1/', errno: 17 "File exists") ERROR HY000: Error dropping database (can't rmdir './mysqltest1', errno: 39 "Directory not empty")
use mysqltest1; use mysqltest1;
show tables; show tables;
Tables_in_mysqltest1 Tables_in_mysqltest1
drop database mysqltest1; drop database mysqltest1;
ERROR HY000: Error dropping database (can't rmdir './mysqltest1/', errno: 17 "File exists") ERROR HY000: Error dropping database (can't rmdir './mysqltest1', errno: 39 "Directory not empty")
use mysqltest1; use mysqltest1;
show tables; show tables;
Tables_in_mysqltest1 Tables_in_mysqltest1
......
...@@ -13,7 +13,7 @@ insert into mysqltest1.t1 values (1); ...@@ -13,7 +13,7 @@ insert into mysqltest1.t1 values (1);
select * from mysqltest1.t1 into outfile 'mysqltest1/f1.txt'; select * from mysqltest1.t1 into outfile 'mysqltest1/f1.txt';
create table mysqltest1.t2 (n int); create table mysqltest1.t2 (n int);
create table mysqltest1.t3 (n int); create table mysqltest1.t3 (n int);
--replace_result \\ / --replace_result \\ / 66 39
--error 1010 --error 1010
drop database mysqltest1; drop database mysqltest1;
use mysqltest1; use mysqltest1;
...@@ -30,7 +30,7 @@ while ($1) ...@@ -30,7 +30,7 @@ while ($1)
} }
--enable_query_log --enable_query_log
--replace_result \\ / --replace_result \\ / 66 39
--error 1010 --error 1010
drop database mysqltest1; drop database mysqltest1;
use mysqltest1; use mysqltest1;
......
...@@ -198,10 +198,10 @@ int extension_based_table_discovery(MY_DIR *dirp, const char *ext_meta, ...@@ -198,10 +198,10 @@ int extension_based_table_discovery(MY_DIR *dirp, const char *ext_meta,
end= cur + dirp->number_of_files; end= cur + dirp->number_of_files;
while (cur < end) while (cur < end)
{ {
char *octothorp= strrchr(cur->name, '#'); char *octothorp= strrchr(cur->name + 1, '#');
char *ext= strchr(octothorp ? octothorp : cur->name, FN_EXTCHAR); char *ext= strchr(octothorp ? octothorp : cur->name, FN_EXTCHAR);
if (ext && octothorp != cur->name) if (ext)
{ {
size_t len= (octothorp ? octothorp : ext) - cur->name; size_t len= (octothorp ? octothorp : ext) - cur->name;
if (from != cur && if (from != cur &&
...@@ -239,13 +239,6 @@ int extension_based_table_discovery(MY_DIR *dirp, const char *ext_meta, ...@@ -239,13 +239,6 @@ int extension_based_table_discovery(MY_DIR *dirp, const char *ext_meta,
simplified version of extension_based_table_discovery(), that does not simplified version of extension_based_table_discovery(), that does not
modify the list of files. It cannot be called many times for the same modify the list of files. It cannot be called many times for the same
directory listing, otherwise it'll produce duplicate results. directory listing, otherwise it'll produce duplicate results.
@note
For backward compatibility reasons, this will find tables with names,
starting from '#', as long as they don't start from '#sql-'.
These names are invalid since 5.0, and the compex discovery function
will ignore them. Anyone still having these files, should disable
discovering engines, and rename these invalid table files.
*/ */
int ext_table_discovery_simple(MY_DIR *dirp, int ext_table_discovery_simple(MY_DIR *dirp,
handlerton::discovered_list *result) handlerton::discovered_list *result)
...@@ -259,7 +252,7 @@ int ext_table_discovery_simple(MY_DIR *dirp, ...@@ -259,7 +252,7 @@ int ext_table_discovery_simple(MY_DIR *dirp,
{ {
char *ext= strrchr(cur->name, FN_EXTCHAR); char *ext= strrchr(cur->name, FN_EXTCHAR);
if (ext && !is_prefix(cur->name, tmp_file_prefix)) if (ext)
{ {
if (my_strnncoll(cs, (uchar*)ext, strlen(ext), if (my_strnncoll(cs, (uchar*)ext, strlen(ext),
(uchar*)reg_ext, reg_ext_length) == 0) (uchar*)reg_ext, reg_ext_length) == 0)
......
...@@ -4634,12 +4634,12 @@ static my_bool discover_names(THD *thd, plugin_ref plugin, ...@@ -4634,12 +4634,12 @@ static my_bool discover_names(THD *thd, plugin_ref plugin,
} }
int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp, int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp,
Discovered_table_list *result) Discovered_table_list *result, bool reusable)
{ {
int error; int error;
DBUG_ENTER("ha_discover_table_names"); DBUG_ENTER("ha_discover_table_names");
if (engines_with_discover_table_names == 0) if (engines_with_discover_table_names == 0 && !reusable)
{ {
error= ext_table_discovery_simple(dirp, result); error= ext_table_discovery_simple(dirp, result);
result->sort(); result->sort();
......
...@@ -3163,7 +3163,7 @@ public: ...@@ -3163,7 +3163,7 @@ public:
int ha_discover_table(THD *thd, TABLE_SHARE *share); int ha_discover_table(THD *thd, TABLE_SHARE *share);
int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp, int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp,
Discovered_table_list *result); Discovered_table_list *result, bool reusable);
bool ha_table_exists(THD *thd, const char *db, const char *table_name, bool ha_table_exists(THD *thd, const char *db, const char *table_name,
handlerton **hton= 0); handlerton **hton= 0);
#endif #endif
......
...@@ -48,13 +48,12 @@ ...@@ -48,13 +48,12 @@
#define MAX_DROP_TABLE_Q_LEN 1024 #define MAX_DROP_TABLE_Q_LEN 1024
const char *del_exts[]= {".frm", ".BAK", ".TMD",".opt", NullS}; const char *del_exts[]= {".BAK", ".TMD",".opt", NullS};
static TYPELIB deletable_extentions= static TYPELIB deletable_extentions=
{array_elements(del_exts)-1,"del_exts", del_exts, NULL}; {array_elements(del_exts)-1,"del_exts", del_exts, NULL};
static bool find_db_tables_and_rm_known_files(THD *, MY_DIR *, const char *, static bool find_db_tables_and_rm_known_files(THD *, MY_DIR *, char *,
const char *, TABLE_LIST **, const char *, TABLE_LIST **);
bool *);
long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path); long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error); static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error);
...@@ -757,7 +756,6 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) ...@@ -757,7 +756,6 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
char path[FN_REFLEN + 16]; char path[FN_REFLEN + 16];
MY_DIR *dirp; MY_DIR *dirp;
uint length; uint length;
bool found_other_files= false;
TABLE_LIST *tables= NULL; TABLE_LIST *tables= NULL;
TABLE_LIST *table; TABLE_LIST *table;
Drop_table_error_handler err_handler; Drop_table_error_handler err_handler;
...@@ -789,8 +787,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) ...@@ -789,8 +787,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
} }
} }
if (find_db_tables_and_rm_known_files(thd, dirp, db, path, &tables, if (find_db_tables_and_rm_known_files(thd, dirp, db, path, &tables))
&found_other_files))
goto exit; goto exit;
/* /*
...@@ -871,10 +868,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) ...@@ -871,10 +868,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
If the directory is a symbolic link, remove the link first, then If the directory is a symbolic link, remove the link first, then
remove the directory the symbolic link pointed at remove the directory the symbolic link pointed at
*/ */
if (found_other_files) error= rm_dir_w_symlink(path, true);
my_error(ER_DB_DROP_RMDIR, MYF(0), path, EEXIST);
else
error= rm_dir_w_symlink(path, true);
} }
thd->pop_internal_handler(); thd->pop_internal_handler();
...@@ -985,18 +979,58 @@ exit: ...@@ -985,18 +979,58 @@ exit:
static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
const char *db, char *dbname,
const char *path, const char *path,
TABLE_LIST **tables, TABLE_LIST **tables)
bool *found_other_files)
{ {
char filePath[FN_REFLEN]; char filePath[FN_REFLEN];
LEX_STRING db= { dbname, strlen(dbname) };
TABLE_LIST *tot_list=0, **tot_list_next_local, **tot_list_next_global; TABLE_LIST *tot_list=0, **tot_list_next_local, **tot_list_next_global;
DBUG_ENTER("find_db_tables_and_rm_known_files"); DBUG_ENTER("find_db_tables_and_rm_known_files");
DBUG_PRINT("enter",("path: %s", path)); DBUG_PRINT("enter",("path: %s", path));
/* first, get the list of tables */
Dynamic_array<LEX_STRING*> files(dirp->number_of_files);
Discovered_table_list tl(thd, &files, &null_lex_str);
if (ha_discover_table_names(thd, &db, dirp, &tl, true))
DBUG_RETURN(1);
/* Now put the tables in the list */
tot_list_next_local= tot_list_next_global= &tot_list; tot_list_next_local= tot_list_next_global= &tot_list;
for (size_t idx=0; idx < files.elements(); idx++)
{
LEX_STRING *table= files.at(idx);
/* Drop the table nicely */
TABLE_LIST *table_list=(TABLE_LIST*)thd->calloc(sizeof(*table_list));
if (!table_list)
DBUG_RETURN(true);
table_list->db= db.str;
table_list->db_length= db.length;
table_list->table_name= table->str;
table_list->table_name_length= table->length;
table_list->open_type= OT_BASE_ONLY;
/* To be able to correctly look up the table in the table cache. */
if (lower_case_table_names)
table_list->table_name_length= my_casedn_str(files_charset_info,
table_list->table_name);
table_list->alias= table_list->table_name; // If lower_case_table_names=2
table_list->mdl_request.init(MDL_key::TABLE, table_list->db,
table_list->table_name, MDL_EXCLUSIVE,
MDL_TRANSACTION);
/* Link into list */
(*tot_list_next_local)= table_list;
(*tot_list_next_global)= table_list;
tot_list_next_local= &table_list->next_local;
tot_list_next_global= &table_list->next_global;
}
*tables= tot_list;
/* and at last delete all non-table files */
for (uint idx=0 ; for (uint idx=0 ;
idx < (uint) dirp->number_of_files && !thd->killed ; idx < (uint) dirp->number_of_files && !thd->killed ;
idx++) idx++)
...@@ -1021,59 +1055,12 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, ...@@ -1021,59 +1055,12 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
DBUG_PRINT("my",("Archive subdir found: %s", newpath)); DBUG_PRINT("my",("Archive subdir found: %s", newpath));
if ((mysql_rm_arc_files(thd, new_dirp, newpath)) < 0) if ((mysql_rm_arc_files(thd, new_dirp, newpath)) < 0)
DBUG_RETURN(true); DBUG_RETURN(true);
continue;
} }
*found_other_files= true;
continue; continue;
} }
if (!(extension= strrchr(file->name, '.'))) if (!(extension= strrchr(file->name, '.')))
extension= strend(file->name); extension= strend(file->name);
if (find_type(extension, &deletable_extentions, FIND_TYPE_NO_PREFIX) <= 0) if (find_type(extension, &deletable_extentions, FIND_TYPE_NO_PREFIX) > 0)
{
if (find_type(extension, ha_known_exts(), FIND_TYPE_NO_PREFIX) <= 0)
*found_other_files= true;
continue;
}
/* just for safety we use files_charset_info */
if (db && !my_strcasecmp(files_charset_info,
extension, reg_ext))
{
/* Drop the table nicely */
*extension= 0; // Remove extension
TABLE_LIST *table_list=(TABLE_LIST*)
thd->calloc(sizeof(*table_list) +
strlen(db) + 1 +
MYSQL50_TABLE_NAME_PREFIX_LENGTH +
strlen(file->name) + 1);
if (!table_list)
DBUG_RETURN(true);
table_list->db= (char*) (table_list+1);
table_list->db_length= strmov(table_list->db, db) - table_list->db;
table_list->table_name= table_list->db + table_list->db_length + 1;
table_list->table_name_length= filename_to_tablename(file->name,
table_list->table_name,
MYSQL50_TABLE_NAME_PREFIX_LENGTH +
strlen(file->name) + 1);
table_list->open_type= OT_BASE_ONLY;
/* To be able to correctly look up the table in the table cache. */
if (lower_case_table_names)
table_list->table_name_length= my_casedn_str(files_charset_info,
table_list->table_name);
table_list->alias= table_list->table_name; // If lower_case_table_names=2
table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix);
table_list->mdl_request.init(MDL_key::TABLE, table_list->db,
table_list->table_name, MDL_EXCLUSIVE,
MDL_TRANSACTION);
/* Link into list */
(*tot_list_next_local)= table_list;
(*tot_list_next_global)= table_list;
tot_list_next_local= &table_list->next_local;
tot_list_next_global= &table_list->next_global;
}
else
{ {
strxmov(filePath, path, "/", file->name, NullS); strxmov(filePath, path, "/", file->name, NullS);
/* /*
...@@ -1088,7 +1075,7 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, ...@@ -1088,7 +1075,7 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
} }
} }
} }
*tables= tot_list;
DBUG_RETURN(false); DBUG_RETURN(false);
} }
......
...@@ -831,7 +831,7 @@ find_files(THD *thd, Dynamic_array<LEX_STRING*> *files, LEX_STRING *db, ...@@ -831,7 +831,7 @@ find_files(THD *thd, Dynamic_array<LEX_STRING*> *files, LEX_STRING *db,
} }
else else
{ {
if (ha_discover_table_names(thd, db, dirp, &tl)) if (ha_discover_table_names(thd, db, dirp, &tl, false))
goto err; goto err;
} }
......
...@@ -391,31 +391,14 @@ uint filename_to_tablename(const char *from, char *to, uint to_length ...@@ -391,31 +391,14 @@ uint filename_to_tablename(const char *from, char *to, uint to_length
DBUG_ENTER("filename_to_tablename"); DBUG_ENTER("filename_to_tablename");
DBUG_PRINT("enter", ("from '%s'", from)); DBUG_PRINT("enter", ("from '%s'", from));
if (!strncmp(from, tmp_file_prefix, tmp_file_prefix_length)) res= strconvert(&my_charset_filename, from,
system_charset_info, to, to_length, &errors);
if (errors) // Old 5.0 name
{ {
/* Temporary table name. */ res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) -
res= (strnmov(to, from, to_length) - to); to);
} if (IF_DBUG(!stay_quiet,0))
else sql_print_error("Invalid (old?) table or database name '%s'", from);
{
res= strconvert(&my_charset_filename, from,
system_charset_info, to, to_length, &errors);
if (errors) // Old 5.0 name
{
res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) -
to);
#ifndef DBUG_OFF
if (!stay_quiet) {
#endif /* DBUG_OFF */
sql_print_error("Invalid (old?) table or database name '%s'", from);
#ifndef DBUG_OFF
}
#endif /* DBUG_OFF */
/*
TODO: add a stored procedure for fix table and database names,
and mention its name in error log.
*/
}
} }
DBUG_PRINT("exit", ("to '%s'", to)); DBUG_PRINT("exit", ("to '%s'", to));
...@@ -2230,9 +2213,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -2230,9 +2213,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
alias= (lower_case_table_names == 2) ? table->alias : table->table_name; alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
/* remove .frm file and engine files */ /* remove .frm file and engine files */
path_length= build_table_filename(path, sizeof(path) - 1, db, alias, path_length= build_table_filename(path, sizeof(path) - 1, db, alias,
reg_ext, reg_ext, 0);
table->internal_tmp_table ?
FN_IS_TMP : 0);
/* /*
This handles the case where a "DROP" was executed and a regular This handles the case where a "DROP" was executed and a regular
...@@ -2266,8 +2247,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -2266,8 +2247,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
} }
DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table");
error= 0; error= 0;
if (!table->internal_tmp_table && if ((drop_temporary || !ha_table_exists(thd, db, alias, &table_type) ||
(drop_temporary || !ha_table_exists(thd, db, alias, &table_type) ||
(!drop_view && table_type == view_pseudo_hton))) (!drop_view && table_type == view_pseudo_hton)))
{ {
/* /*
...@@ -2276,10 +2256,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -2276,10 +2256,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
. "DROP" but table was not found on disk and table can't be . "DROP" but table was not found on disk and table can't be
created from engine. created from engine.
. ./sql/datadict.cc +32 /Alfranio - TODO: We need to test this. . ./sql/datadict.cc +32 /Alfranio - TODO: We need to test this.
Table->internal_tmp_table is set when one of the #sql-xxx files
was left in the datadir after a crash during ALTER TABLE.
See Bug#30152.
*/ */
if (if_exists) if (if_exists)
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
...@@ -2294,7 +2270,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -2294,7 +2270,6 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
else else
{ {
char *end; char *end;
/* /*
It could happen that table's share in the table_def_cache It could happen that table's share in the table_def_cache
is the only thing that keeps the engine plugin loaded is the only thing that keeps the engine plugin loaded
......
...@@ -1983,7 +1983,6 @@ struct TABLE_LIST ...@@ -1983,7 +1983,6 @@ struct TABLE_LIST
/* For transactional locking. */ /* For transactional locking. */
int lock_timeout; /* NOWAIT or WAIT [X] */ int lock_timeout; /* NOWAIT or WAIT [X] */
bool lock_transactional; /* If transactional lock requested. */ bool lock_transactional; /* If transactional lock requested. */
bool internal_tmp_table;
/** TRUE if an alias for this table was specified in the SQL. */ /** TRUE if an alias for this table was specified in the SQL. */
bool is_alias; bool is_alias;
/** TRUE if the table is referred to in the statement using a fully /** TRUE if the table is referred to in the statement using a fully
......
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