Skip to content

Commit

Permalink
MDEV-11412 Ensure that table is truly dropped when using DROP TABLE
Browse files Browse the repository at this point in the history
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
  • Loading branch information
montywi committed Jun 14, 2020
1 parent 5579c38 commit 5bcb1d6
Show file tree
Hide file tree
Showing 32 changed files with 870 additions and 199 deletions.
1 change: 1 addition & 0 deletions include/my_sys.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ C_MODE_START
#define MY_WME 16U /* Write message on error */
#define MY_WAIT_IF_FULL 32U /* Wait and try again if disk full error */
#define MY_IGNORE_BADFD 32U /* my_sync(): ignore 'bad descriptor' errors */
#define MY_IGNORE_ENOENT 32U /* my_delete() ignores ENOENT (no such file) */
#define MY_ENCRYPT 64U /* Encrypt IO_CACHE temporary files */
#define MY_TEMPORARY 64U /* create_temp_file(): delete file at once */
#define MY_NOSYMLINKS 512U /* my_open(): don't follow symlinks */
Expand Down
6 changes: 6 additions & 0 deletions mysql-test/main/create_drop_view.result
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,14 @@ id
50
80
40
DROP TABLE IF EXISTS v1;
Warnings:
Note 1051 Unknown table 'test.v1'
DROP VIEW IF EXISTS v1;
DROP VIEW IF EXISTS v1;
Warnings:
Note 4092 Unknown VIEW: 'test.v1'
DROP VIEW IF EXISTS t1;
Warnings:
Note 4092 Unknown VIEW: 'test.t1'
DROP TABLE t1;
2 changes: 2 additions & 0 deletions mysql-test/main/create_drop_view.test
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ INSERT INTO t1 VALUES (50), (80), (3), (2), (40);
SELECT * FROM t1;
SELECT * FROM v1;

DROP TABLE IF EXISTS v1;
DROP VIEW IF EXISTS v1;
DROP VIEW IF EXISTS v1;
DROP VIEW IF EXISTS t1;
DROP TABLE t1;
133 changes: 133 additions & 0 deletions mysql-test/main/drop_table_force.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
CALL mtr.add_suppression("Operating system error number");
CALL mtr.add_suppression("The error means the system cannot");
CALL mtr.add_suppression("returned OS error 71");
#Test1: table with missing .ibd can be dropped directly
create table t1(a int)engine=innodb;
drop table t1;
db.opt
# Test droping table without frm without super privilege
create table t1(a int) engine=innodb;
create user test identified by '123456';
grant all privileges on test.t1 to 'test'@'%'identified by '123456' with grant option;
connect con_test, localhost, test,'123456', ;
connection con_test;
drop table t1;
drop table t1;
ERROR 42S02: Unknown table 'test.t1'
connection default;
disconnect con_test;
drop user test;
db.opt
#Test4: drop table can drop consistent table as well
create table t1(a int) engine=innodb;
drop table t1;
db.opt
#Test5: drop table with triger, and with missing frm
create table t1(a int)engine=innodb;
create trigger t1_trg before insert on t1 for each row begin end;
drop table t1;
drop table t1;
ERROR 42S02: Unknown table 'test.t1'
db.opt
#Test6: table with foreign key references can not be dropped
CREATE TABLE parent (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB;
CREATE TABLE child (id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE) ENGINE=INNODB;
drop table parent;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
drop table child;
drop table parent;
db.opt
#Test7: drop table twice
create table t1(a int)engine=innodb;
drop table t1;
db.opt
drop table if exists t1;
Warnings:
Note 1051 Unknown table 'test.t1'
db.opt
#Test8: check compatibility with if exists
create table t1(a int)engine=innodb;
drop table t1;
db.opt
drop table if exists t1;
Warnings:
Note 1051 Unknown table 'test.t1'
#Test9: check compatibility with restrict/cascade
CREATE TABLE parent (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB;
CREATE TABLE child (id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE) ENGINE=INNODB;
drop table parent;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
drop table parent restrict;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
drop table parent cascade;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
drop table parent;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
drop table parent restrict;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
drop table parent cascade;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
drop table child;
drop table parent;
#Test10: drop non-innodb engine table returns ok
create table t1(a int) engine=myisam;
drop table t1;
create table t1(a int) engine=myisam;
drop table t1;
Warnings:
Warning 1017 Can't find file: './test/t1.MYI' (errno: 2 "No such file or directory")
create table t1(a int) engine=myisam;
drop table t1;
Warnings:
Warning 1017 Can't find file: './test/t1.MYI' (errno: 2 "No such file or directory")
db.opt
create table t1(a int) engine=aria;
db.opt
t1.MAI
drop table t1;
ERROR 42S02: Unknown table 'test.t1'
show warnings;
Level Code Message
Error 29 File './test/t1.MAD' not found (Errcode: 2 "No such file or directory")
Error 1051 Unknown table 'test.t1'
db.opt
create table t2(a int) engine=aria;
flush tables;
db.opt
t2.MAD
drop table t2;
ERROR 42S02: Unknown table 'test.t2'
show warnings;
Level Code Message
Error 1051 Unknown table 'test.t2'
db.opt
create table t2(a int) engine=aria;
flush tables;
db.opt
t2.frm
drop table t2;
Warnings:
Warning 1017 Can't find file: './test/t2.MAI' (errno: 2 "No such file or directory")
create table t2(a int not null) engine=CSV;
flush tables;
drop table t2;
db.opt
create table t2(a int not null) engine=CSV;
flush tables;
drop table t2;
db.opt
create table t2(a int not null) engine=archive;
flush tables;
select * from t2;
a
flush tables;
select * from t2;
ERROR 42S02: Table 'test.t2' doesn't exist
db.opt
drop table t2;
ERROR 42S02: Unknown table 'test.t2'
create table t2(a int not null) engine=archive;
flush tables;
drop table t2;
ERROR 42S02: Unknown table 'test.t2'
db.opt
Loading

0 comments on commit 5bcb1d6

Please sign in to comment.