Skip to content

Commit b26e603

Browse files
committed
MDEV-17859 Operating system errors in file operations after failed CREATE
This is a regression due to MDEV-17816. When creating a table fails, we must roll back the dictionary transaction. Because the rollback may rename tables, and because InnoDB lacks proper undo logging for CREATE operations, we must drop the incompletely created table before rolling back the transaction, which could include a RENAME operation. But, we must not blindly drop the table by name; after all, the operation could have failed because another table by the same name already existed. create_table_info_t::m_drop_before_rollback: A flag that is set if the table needs to be dropped before transaction rollback. create_table_info_t::create_table(): Remove some duplicated error handling. ha_innobase::create(): On error, only drop the table if it was actually created.
1 parent 0485e51 commit b26e603

File tree

4 files changed

+52
-14
lines changed

4 files changed

+52
-14
lines changed

mysql-test/suite/innodb/r/truncate.result

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,18 @@ SHOW TABLE STATUS;
2727
Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment
2828
t1 InnoDB # Compressed # # # # # # 1 # # NULL latin1_swedish_ci NULL key_block_size=4
2929
DROP TABLE t1;
30+
#
31+
# MDEV-17859 Operating system errors in file operations
32+
# after failed CREATE
33+
#
34+
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
35+
INSERT INTO t1 VALUES (1);
36+
call mtr.add_suppression("InnoDB: (Operating system )?[Ee]rror number");
37+
call mtr.add_suppression("InnoDB: Cannot create file '.*t1\\.ibd");
38+
FLUSH TABLES;
39+
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
40+
ERROR HY000: Tablespace for table '`test`.`t1`' exists. Please DISCARD the tablespace before IMPORT
41+
SELECT * FROM t1;
42+
a
43+
1
44+
DROP TABLE t1;

mysql-test/suite/innodb/t/truncate.test

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,20 @@ TRUNCATE TABLE t1;
3636
--replace_column 3 # 5 # 6 # 7 # 8 # 9 # 10 # 12 # 13 #
3737
SHOW TABLE STATUS;
3838
DROP TABLE t1;
39+
40+
--echo #
41+
--echo # MDEV-17859 Operating system errors in file operations
42+
--echo # after failed CREATE
43+
--echo #
44+
let $MYSQLD_DATADIR= `select @@datadir`;
45+
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
46+
INSERT INTO t1 VALUES (1);
47+
call mtr.add_suppression("InnoDB: (Operating system )?[Ee]rror number");
48+
call mtr.add_suppression("InnoDB: Cannot create file '.*t1\\.ibd");
49+
FLUSH TABLES;
50+
--move_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/hidden.frm
51+
--error ER_TABLESPACE_EXISTS
52+
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
53+
--move_file $MYSQLD_DATADIR/test/hidden.frm $MYSQLD_DATADIR/test/t1.frm
54+
SELECT * FROM t1;
55+
DROP TABLE t1;

storage/innobase/handler/ha_innodb.cc

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10966,6 +10966,7 @@ create_table_info_t::create_table_def()
1096610966
DBUG_PRINT("enter", ("table_name: %s", m_table_name));
1096710967

1096810968
DBUG_ASSERT(m_trx->mysql_thd == m_thd);
10969+
DBUG_ASSERT(!m_drop_before_rollback);
1096910970

1097010971
/* MySQL does the name length check. But we do additional check
1097110972
on the name length here */
@@ -11228,6 +11229,7 @@ create_table_info_t::create_table_def()
1122811229
table, m_trx,
1122911230
(fil_encryption_t)options->encryption,
1123011231
(uint32_t)options->encryption_key_id);
11232+
m_drop_before_rollback = (err == DB_SUCCESS);
1123111233
}
1123211234

1123311235
DBUG_EXECUTE_IF("ib_crash_during_create_for_encryption",
@@ -12531,6 +12533,9 @@ int create_table_info_t::create_table(bool create_fk)
1253112533
DBUG_RETURN(error);
1253212534
}
1253312535

12536+
DBUG_ASSERT(m_drop_before_rollback
12537+
== !(m_flags2 & DICT_TF2_TEMPORARY));
12538+
1253412539
/* Create the keys */
1253512540

1253612541
if (m_form->s->keys == 0 || primary_key_no == -1) {
@@ -12591,6 +12596,7 @@ int create_table_info_t::create_table(bool create_fk)
1259112596
dict_table_close(innobase_table, TRUE, FALSE);
1259212597
my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0),
1259312598
FTS_DOC_ID_INDEX_NAME);
12599+
m_drop_before_rollback = false;
1259412600
error = -1;
1259512601
DBUG_RETURN(error);
1259612602
case FTS_EXIST_DOC_ID_INDEX:
@@ -12607,16 +12613,6 @@ int create_table_info_t::create_table(bool create_fk)
1260712613
dict_table_close(innobase_table, TRUE, FALSE);
1260812614

1260912615
if (error) {
12610-
/* Drop the being-created table before rollback,
12611-
so that rollback can possibly rename back a table
12612-
that could have been renamed before
12613-
the failed creation. */
12614-
m_trx->error_state = DB_SUCCESS;
12615-
row_drop_table_for_mysql(m_table_name, m_trx,
12616-
SQLCOM_TRUNCATE);
12617-
trx_rollback_to_savepoint(m_trx, NULL);
12618-
12619-
m_trx->error_state = DB_SUCCESS;
1262012616
DBUG_RETURN(error);
1262112617
}
1262212618
}
@@ -12686,6 +12682,9 @@ int create_table_info_t::create_table(bool create_fk)
1268612682
error = convert_error_code_to_mysql(err, m_flags, NULL);
1268712683

1268812684
if (error) {
12685+
/* row_table_add_foreign_constraints() dropped
12686+
the table */
12687+
m_drop_before_rollback = false;
1268912688
DBUG_RETURN(error);
1269012689
}
1269112690
}
@@ -12859,9 +12858,11 @@ ha_innobase::create(
1285912858
/* Drop the being-created table before rollback,
1286012859
so that rollback can possibly rename back a table
1286112860
that could have been renamed before the failed creation. */
12862-
trx->error_state = DB_SUCCESS;
12863-
row_drop_table_for_mysql(info.table_name(), trx,
12864-
SQLCOM_TRUNCATE, true);
12861+
if (info.drop_before_rollback()) {
12862+
trx->error_state = DB_SUCCESS;
12863+
row_drop_table_for_mysql(info.table_name(),
12864+
trx, SQLCOM_TRUNCATE, true);
12865+
}
1286512866
trx_rollback_for_mysql(trx);
1286612867
row_mysql_unlock_data_dictionary(trx);
1286712868
if (own_trx) {

storage/innobase/handler/ha_innodb.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ class create_table_info_t
646646
m_trx(trx),
647647
m_form(form),
648648
m_create_info(create_info),
649-
m_table_name(table_name),
649+
m_table_name(table_name), m_drop_before_rollback(false),
650650
m_remote_path(remote_path),
651651
m_innodb_file_per_table(file_per_table)
652652
{}
@@ -719,6 +719,9 @@ class create_table_info_t
719719
const char* table_name() const
720720
{ return(m_table_name); }
721721

722+
/** @return whether the table needs to be dropped on rollback */
723+
bool drop_before_rollback() const { return m_drop_before_rollback; }
724+
722725
THD* thd() const
723726
{ return(m_thd); }
724727

@@ -760,6 +763,8 @@ class create_table_info_t
760763

761764
/** Table name */
762765
char* m_table_name;
766+
/** Whether the table needs to be dropped before rollback */
767+
bool m_drop_before_rollback;
763768

764769
/** Remote path (DATA DIRECTORY) or zero length-string */
765770
char* m_remote_path;

0 commit comments

Comments
 (0)