mirror of
https://github.com/MariaDB/server.git
synced 2025-01-15 19:42:28 +01:00
5bcb1d6532
The used code is largely based on code from Tencent The problem is that in some rare cases there may be a conflict between .frm files and the files in the storage engine. In this case the DROP TABLE was not able to properly drop the table. Some MariaDB/MySQL forks has solved this by adding a FORCE option to DROP TABLE. After some discussion among MariaDB developers, we concluded that users expects that DROP TABLE should always work, even if the table would not be consistent. There should not be a need to use a separate keyword to ensure that the table is really deleted. The used solution is: - If a .frm table doesn't exists, try dropping the table from all storage engines. - If the .frm table exists but the table does not exist in the engine try dropping the table from all storage engines. - Update storage engines using many table files (.CVS, MyISAM, Aria) to succeed with the drop even if some of the files are missing. - Add HTON_AUTOMATIC_DELETE_TABLE to handlerton's where delete_table() is not needed and always succeed. This is used by ha_delete_table_force() to know which handlers to ignore when trying to drop a table without a .frm file. The disadvantage of this solution is that a DROP TABLE on a non existing table will be a bit slower as we have to ask all active storage engines if they know anything about the table. Other things: - Added a new flag MY_IGNORE_ENOENT to my_delete() to not give an error if the file doesn't exist. This simplifies some of the code. - Don't clear thd->error in ha_delete_table() if there was an active error. This is a bug fix. - handler::delete_table() will not abort if first file doesn't exists. This is bug fix to handle the case when a drop table was aborted in the middle. - Cleaned up mysql_rm_table_no_locks() to ensure that if_exists uses same code path as when it's not used. - Use non_existing_Table_error() to detect if table didn't exists. Old code used different errors tests in different position. - Table_triggers_list::drop_all_triggers() now drops trigger file if it can't be parsed instead of leaving it hanging around (bug fix) - InnoDB doesn't anymore print error about .frm file out of sync with InnoDB directory if .frm file does not exists. This change was required to be able to try to drop an InnoDB file when .frm doesn't exists. - Fixed bug in mi_delete_table() where the .MYD file would not be dropped if the .MYI file didn't exists. - Fixed memory leak in Mroonga when deleting non existing table - Fixed memory leak in Connect when deleting non existing table Bugs fixed introduced by the original version of this commit: MDEV-22826 Presence of Spider prevents tables from being force-deleted from other engines
191 lines
5.4 KiB
C
191 lines
5.4 KiB
C
/* Copyright (c) 2000, 2001, 2003, 2005-2007 MySQL AB
|
|
Use is subject to license terms
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
|
|
|
|
/*
|
|
Advanced symlink handling.
|
|
This is used in MyISAM to let users symlinks tables to different disk.
|
|
The main idea with these functions is to automatically create, delete and
|
|
rename files and symlinks like they would be one unit.
|
|
*/
|
|
|
|
#include "mysys_priv.h"
|
|
#include "mysys_err.h"
|
|
#include <m_string.h>
|
|
|
|
File my_create_with_symlink(const char *linkname, const char *filename,
|
|
int createflags, int access_flags, myf MyFlags)
|
|
{
|
|
File file;
|
|
int tmp_errno;
|
|
/* Test if we should create a link */
|
|
int create_link;
|
|
char abs_linkname[FN_REFLEN];
|
|
DBUG_ENTER("my_create_with_symlink");
|
|
DBUG_PRINT("enter", ("linkname: %s filename: %s",
|
|
linkname ? linkname : "(NULL)",
|
|
filename ? filename : "(NULL)"));
|
|
|
|
if (my_disable_symlinks)
|
|
{
|
|
DBUG_PRINT("info", ("Symlinks disabled"));
|
|
/* Create only the file, not the link and file */
|
|
create_link= 0;
|
|
if (linkname)
|
|
filename= linkname;
|
|
}
|
|
else
|
|
{
|
|
if (linkname)
|
|
my_realpath(abs_linkname, linkname, MYF(0));
|
|
create_link= (linkname && strcmp(abs_linkname,filename));
|
|
}
|
|
|
|
if (!(MyFlags & MY_DELETE_OLD))
|
|
{
|
|
if (!access(filename,F_OK))
|
|
{
|
|
my_errno= errno= EEXIST;
|
|
my_error(EE_CANTCREATEFILE, MYF(0), filename, EEXIST);
|
|
DBUG_RETURN(-1);
|
|
}
|
|
if (create_link && !access(linkname,F_OK))
|
|
{
|
|
my_errno= errno= EEXIST;
|
|
my_error(EE_CANTCREATEFILE, MYF(0), linkname, EEXIST);
|
|
DBUG_RETURN(-1);
|
|
}
|
|
}
|
|
|
|
if ((file=my_create(filename, createflags, access_flags, MyFlags)) >= 0)
|
|
{
|
|
if (create_link)
|
|
{
|
|
/* Delete old link/file */
|
|
if (MyFlags & MY_DELETE_OLD)
|
|
my_delete(linkname, MYF(0));
|
|
/* Create link */
|
|
if (my_symlink(filename, linkname, MyFlags))
|
|
{
|
|
/* Fail, remove everything we have done */
|
|
tmp_errno=my_errno;
|
|
my_close(file,MYF(0));
|
|
my_delete(filename, MYF(0));
|
|
file= -1;
|
|
my_errno=tmp_errno;
|
|
}
|
|
}
|
|
}
|
|
DBUG_RETURN(file);
|
|
}
|
|
|
|
/*
|
|
If the file is a normal file, just rename it.
|
|
If the file is a symlink:
|
|
- Create a new file with the name 'to' that points at
|
|
symlink_dir/basename(to)
|
|
- Rename the symlinked file to symlink_dir/basename(to)
|
|
- Delete 'from'
|
|
If something goes wrong, restore everything.
|
|
*/
|
|
|
|
int my_rename_with_symlink(const char *from, const char *to, myf MyFlags)
|
|
{
|
|
#ifndef HAVE_READLINK
|
|
return my_rename(from, to, MyFlags);
|
|
#else
|
|
char link_name[FN_REFLEN], tmp_name[FN_REFLEN];
|
|
int was_symlink= (!my_disable_symlinks &&
|
|
!my_readlink(link_name, from, MYF(0)));
|
|
int result=0;
|
|
int name_is_different;
|
|
DBUG_ENTER("my_rename_with_symlink");
|
|
|
|
if (!was_symlink)
|
|
DBUG_RETURN(my_rename(from, to, MyFlags));
|
|
|
|
/* Change filename that symlink pointed to */
|
|
strmov(tmp_name, to);
|
|
fn_same(tmp_name,link_name,1); /* Copy dir */
|
|
name_is_different= strcmp(link_name, tmp_name);
|
|
if (name_is_different && !access(tmp_name, F_OK))
|
|
{
|
|
my_errno= EEXIST;
|
|
if (MyFlags & MY_WME)
|
|
my_error(EE_CANTCREATEFILE, MYF(0), tmp_name, EEXIST);
|
|
DBUG_RETURN(1);
|
|
}
|
|
|
|
/* Create new symlink */
|
|
if (my_symlink(tmp_name, to, MyFlags))
|
|
DBUG_RETURN(1);
|
|
|
|
/*
|
|
Rename symlinked file if the base name didn't change.
|
|
This can happen if you use this function where 'from' and 'to' has
|
|
the same basename and different directories.
|
|
*/
|
|
|
|
if (name_is_different && my_rename(link_name, tmp_name, MyFlags))
|
|
{
|
|
int save_errno=my_errno;
|
|
my_delete(to, MyFlags); /* Remove created symlink */
|
|
my_errno=save_errno;
|
|
DBUG_RETURN(1);
|
|
}
|
|
|
|
/* Remove original symlink */
|
|
if (my_delete(from, MyFlags))
|
|
{
|
|
int save_errno=my_errno;
|
|
/* Remove created link */
|
|
my_delete(to, MyFlags);
|
|
/* Rename file back */
|
|
if (strcmp(link_name, tmp_name))
|
|
(void) my_rename(tmp_name, link_name, MyFlags);
|
|
my_errno=save_errno;
|
|
result= 1;
|
|
}
|
|
DBUG_RETURN(result);
|
|
#endif /* HAVE_READLINK */
|
|
}
|
|
|
|
/** delete a - possibly symlinked - table file
|
|
|
|
This is used to delete a file that is part of a table (e.g. MYI or MYD
|
|
file of MyISAM) when dropping a table. A file might be a symlink -
|
|
if the table was created with DATA DIRECTORY or INDEX DIRECTORY -
|
|
in this case both the symlink and the symlinked file are deleted,
|
|
but only if the symlinked file is not in the datadir.
|
|
*/
|
|
|
|
int my_handler_delete_with_symlink(const char *filename, myf sync_dir)
|
|
{
|
|
char real[FN_REFLEN];
|
|
int res= 0;
|
|
DBUG_ENTER("my_handler_delete_with_symlink");
|
|
|
|
if (my_is_symlink(filename))
|
|
{
|
|
/*
|
|
Delete the symlinked file only if the symlink is not
|
|
pointing into datadir.
|
|
*/
|
|
if (!(my_realpath(real, filename, MYF(0)) ||
|
|
mysys_test_invalid_symlink(real)))
|
|
res= my_delete(real, MYF(MY_NOSYMLINKS | sync_dir));
|
|
}
|
|
DBUG_RETURN(my_delete(filename, sync_dir) || res);
|
|
}
|