From 2a31b82831c84aae7f23d94a8c646cd9d4d613c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 26 Nov 2018 12:50:27 +0200 Subject: [PATCH] MDEV-17816 Crash in TRUNCATE TABLE when table creation fails The error handling in the MDEV-13564 TRUNCATE TABLE was broken when an error occurred during table creation. row_create_index_for_mysql(): Do not drop the table on error. fts_create_one_common_table(), fts_create_one_index_table(): Do drop the table on error. create_index(), create_table_info_t::create_table(): Let the caller handle the index creation errors. ha_innobase::create(): If create_table_info_t::create_table() fails, drop the incomplete table, roll back the transaction, and finally return an error to the caller. --- mysql-test/suite/innodb/r/truncate.result | 12 +++++++++++ mysql-test/suite/innodb/t/truncate.test | 12 +++++++++++ storage/innobase/fts/fts0fts.cc | 12 +++++++---- storage/innobase/handler/ha_innodb.cc | 15 ++++++++++--- storage/innobase/include/row0mysql.h | 5 ++--- storage/innobase/row/row0mysql.cc | 26 ++--------------------- 6 files changed, 48 insertions(+), 34 deletions(-) diff --git a/mysql-test/suite/innodb/r/truncate.result b/mysql-test/suite/innodb/r/truncate.result index 3ade1e7f8de..c8a81256d79 100644 --- a/mysql-test/suite/innodb/r/truncate.result +++ b/mysql-test/suite/innodb/r/truncate.result @@ -6,3 +6,15 @@ connection default; TRUNCATE TABLE t; disconnect dml; DROP TABLE t; +# +# MDEV-17816 Crash in TRUNCATE TABLE when table creation fails +# +CREATE TABLE t1 (c VARCHAR(1024), KEY(c)) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +INSERT INTO t1 SET c='character'; +ALTER TABLE t1 ROW_FORMAT=REDUNDANT; +TRUNCATE TABLE t1; +ERROR HY000: Index column size too large. The maximum column size is 767 bytes +SELECT * FROM t1; +c +character +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/truncate.test b/mysql-test/suite/innodb/t/truncate.test index cf71ca83c4c..f0cfd3a5be1 100644 --- a/mysql-test/suite/innodb/t/truncate.test +++ b/mysql-test/suite/innodb/t/truncate.test @@ -15,3 +15,15 @@ TRUNCATE TABLE t; disconnect dml; DROP TABLE t; + +--echo # +--echo # MDEV-17816 Crash in TRUNCATE TABLE when table creation fails +--echo # +CREATE TABLE t1 (c VARCHAR(1024), KEY(c)) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +INSERT INTO t1 SET c='character'; +# FIXME: MDEV-17833 ALTER TABLE is not enforcing prefix index size limit +ALTER TABLE t1 ROW_FORMAT=REDUNDANT; +--error ER_INDEX_COLUMN_TOO_LONG +TRUNCATE TABLE t1; +SELECT * FROM t1; +DROP TABLE t1; diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index 7d6dd75ced8..e217758b651 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -1771,7 +1771,7 @@ fts_create_one_common_table( const char* fts_suffix, mem_heap_t* heap) { - dict_table_t* new_table = NULL; + dict_table_t* new_table; dberr_t error; bool is_config = strcmp(fts_suffix, "CONFIG") == 0; @@ -1823,11 +1823,13 @@ fts_create_one_common_table( } if (error != DB_SUCCESS) { - trx->error_state = error; dict_mem_table_free(new_table); new_table = NULL; ib::warn() << "Failed to create FTS common table " << fts_table_name; + trx->error_state = DB_SUCCESS; + row_drop_table_for_mysql(fts_table_name, trx, SQLCOM_DROP_DB); + trx->error_state = error; } return(new_table); } @@ -1969,7 +1971,7 @@ fts_create_one_index_table( mem_heap_t* heap) { dict_field_t* field; - dict_table_t* new_table = NULL; + dict_table_t* new_table; char table_name[MAX_FULL_NAME_LEN]; dberr_t error; CHARSET_INFO* charset; @@ -2032,11 +2034,13 @@ fts_create_one_index_table( } if (error != DB_SUCCESS) { - trx->error_state = error; dict_mem_table_free(new_table); new_table = NULL; ib::warn() << "Failed to create FTS index table " << table_name; + trx->error_state = DB_SUCCESS; + row_drop_table_for_mysql(table_name, trx, SQLCOM_DROP_DB); + trx->error_state = error; } return(new_table); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index e6c57b015a0..262493dd3b0 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -12589,11 +12589,14 @@ int create_table_info_t::create_table(bool create_fk) dict_table_close(innobase_table, TRUE, FALSE); if (error) { - trx_rollback_to_savepoint(m_trx, NULL); + /* Drop the being-created table before rollback, + so that rollback can possibly rename back a table + that could have been renamed before + the failed creation. */ m_trx->error_state = DB_SUCCESS; - row_drop_table_for_mysql(m_table_name, m_trx, SQLCOM_DROP_DB); + trx_rollback_to_savepoint(m_trx, NULL); m_trx->error_state = DB_SUCCESS; DBUG_RETURN(error); @@ -12835,12 +12838,18 @@ ha_innobase::create( } if ((error = info.create_table(own_trx))) { + /* Drop the being-created table before rollback, + so that rollback can possibly rename back a table + that could have been renamed before the failed creation. */ + trx->error_state = DB_SUCCESS; + row_drop_table_for_mysql(info.table_name(), trx, + SQLCOM_DROP_DB, true); trx_rollback_for_mysql(trx); row_mysql_unlock_data_dictionary(trx); if (own_trx) { trx_free_for_mysql(trx); - DBUG_RETURN(error); } + DBUG_RETURN(error); } innobase_commit_low(trx); diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index f3fdf5b4c42..b46d111b8bb 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -359,9 +359,8 @@ row_create_table_for_mysql( MY_ATTRIBUTE((warn_unused_result)); /*********************************************************************//** -Does an index creation operation for MySQL. TODO: currently failure -to create an index results in dropping the whole table! This is no problem -currently as all indexes must be created at the same time as the table. +Create an index when creating a table. +On failure, the caller must drop the table! @return error number or DB_SUCCESS */ dberr_t row_create_index_for_mysql( diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index b098617afda..d79d544a88c 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -2354,9 +2354,8 @@ err_exit: } /*********************************************************************//** -Does an index creation operation for MySQL. TODO: currently failure -to create an index results in dropping the whole table! This is no problem -currently as all indexes must be created at the same time as the table. +Create an index when creating a table. +On failure, the caller must drop the table! @return error number or DB_SUCCESS */ dberr_t row_create_index_for_mysql( @@ -2490,27 +2489,6 @@ row_create_index_for_mysql( error_handling: dict_table_close(table, TRUE, FALSE); - if (err != DB_SUCCESS) { - /* We have special error handling here */ - - trx->error_state = DB_SUCCESS; - - if (trx_is_started(trx)) { - - trx_rollback_to_savepoint(trx, NULL); - } - - row_drop_table_for_mysql(table_name, trx, SQLCOM_DROP_TABLE, - true); - - if (trx_is_started(trx)) { - - trx_commit_for_mysql(trx); - } - - trx->error_state = DB_SUCCESS; - } - trx->op_info = ""; ut_free(table_name);