1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

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.
This commit is contained in:
Marko Mäkelä
2018-11-26 12:50:27 +02:00
parent a81fceafb1
commit 2a31b82831
6 changed files with 48 additions and 34 deletions

View File

@ -6,3 +6,15 @@ connection default;
TRUNCATE TABLE t; TRUNCATE TABLE t;
disconnect dml; disconnect dml;
DROP TABLE t; 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;

View File

@ -15,3 +15,15 @@ TRUNCATE TABLE t;
disconnect dml; disconnect dml;
DROP TABLE t; 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;

View File

@ -1771,7 +1771,7 @@ fts_create_one_common_table(
const char* fts_suffix, const char* fts_suffix,
mem_heap_t* heap) mem_heap_t* heap)
{ {
dict_table_t* new_table = NULL; dict_table_t* new_table;
dberr_t error; dberr_t error;
bool is_config = strcmp(fts_suffix, "CONFIG") == 0; bool is_config = strcmp(fts_suffix, "CONFIG") == 0;
@ -1823,11 +1823,13 @@ fts_create_one_common_table(
} }
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
trx->error_state = error;
dict_mem_table_free(new_table); dict_mem_table_free(new_table);
new_table = NULL; new_table = NULL;
ib::warn() << "Failed to create FTS common table " ib::warn() << "Failed to create FTS common table "
<< fts_table_name; << 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); return(new_table);
} }
@ -1969,7 +1971,7 @@ fts_create_one_index_table(
mem_heap_t* heap) mem_heap_t* heap)
{ {
dict_field_t* field; dict_field_t* field;
dict_table_t* new_table = NULL; dict_table_t* new_table;
char table_name[MAX_FULL_NAME_LEN]; char table_name[MAX_FULL_NAME_LEN];
dberr_t error; dberr_t error;
CHARSET_INFO* charset; CHARSET_INFO* charset;
@ -2032,11 +2034,13 @@ fts_create_one_index_table(
} }
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
trx->error_state = error;
dict_mem_table_free(new_table); dict_mem_table_free(new_table);
new_table = NULL; new_table = NULL;
ib::warn() << "Failed to create FTS index table " ib::warn() << "Failed to create FTS index table "
<< table_name; << 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); return(new_table);

View File

@ -12589,11 +12589,14 @@ int create_table_info_t::create_table(bool create_fk)
dict_table_close(innobase_table, TRUE, FALSE); dict_table_close(innobase_table, TRUE, FALSE);
if (error) { 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; m_trx->error_state = DB_SUCCESS;
row_drop_table_for_mysql(m_table_name, m_trx, row_drop_table_for_mysql(m_table_name, m_trx,
SQLCOM_DROP_DB); SQLCOM_DROP_DB);
trx_rollback_to_savepoint(m_trx, NULL);
m_trx->error_state = DB_SUCCESS; m_trx->error_state = DB_SUCCESS;
DBUG_RETURN(error); DBUG_RETURN(error);
@ -12835,12 +12838,18 @@ ha_innobase::create(
} }
if ((error = info.create_table(own_trx))) { 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); trx_rollback_for_mysql(trx);
row_mysql_unlock_data_dictionary(trx); row_mysql_unlock_data_dictionary(trx);
if (own_trx) { if (own_trx) {
trx_free_for_mysql(trx); trx_free_for_mysql(trx);
DBUG_RETURN(error);
} }
DBUG_RETURN(error);
} }
innobase_commit_low(trx); innobase_commit_low(trx);

View File

@ -359,9 +359,8 @@ row_create_table_for_mysql(
MY_ATTRIBUTE((warn_unused_result)); MY_ATTRIBUTE((warn_unused_result));
/*********************************************************************//** /*********************************************************************//**
Does an index creation operation for MySQL. TODO: currently failure Create an index when creating a table.
to create an index results in dropping the whole table! This is no problem On failure, the caller must drop the table!
currently as all indexes must be created at the same time as the table.
@return error number or DB_SUCCESS */ @return error number or DB_SUCCESS */
dberr_t dberr_t
row_create_index_for_mysql( row_create_index_for_mysql(

View File

@ -2354,9 +2354,8 @@ err_exit:
} }
/*********************************************************************//** /*********************************************************************//**
Does an index creation operation for MySQL. TODO: currently failure Create an index when creating a table.
to create an index results in dropping the whole table! This is no problem On failure, the caller must drop the table!
currently as all indexes must be created at the same time as the table.
@return error number or DB_SUCCESS */ @return error number or DB_SUCCESS */
dberr_t dberr_t
row_create_index_for_mysql( row_create_index_for_mysql(
@ -2490,27 +2489,6 @@ row_create_index_for_mysql(
error_handling: error_handling:
dict_table_close(table, TRUE, FALSE); 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 = ""; trx->op_info = "";
ut_free(table_name); ut_free(table_name);