Commit dcd66c38 authored by Aleksey Midenkov's avatar Aleksey Midenkov

MDEV-29544 SIGSEGV in HA_CREATE_INFO::finalize_locked_tables

Usually when we get into finalize_locked_tables() with error
m_locked_tables_count was not decremented. m_locked_tables_count is
decremented when we drop the original table and if we failed that
m_locked_tables_count is expected intact.

The bug comes from the fact that finalize_atomic_replace() violates
the above contract. It does HA_EXTRA_PREPARE_FOR_DROP and decrements
m_locked_tables_count. Then it tries rename_table_and_triggers() and
fails. With decremented m_locked_tables_count reopen_tables() does
nothing and we don't get new value for pos_in_locked_tables->table.

The test case demonstrates ER_ERROR_ON_RENAME where non-atomic CREATE
OR REPLACE would not fail. The original RENAME TABLE fails under such
broken environment, so nothing is wrong with atomic CREATE OR REPLACE
failing there too.
parent 07581249
......@@ -891,3 +891,24 @@ select * from information_schema.innodb_sys_foreign;
ID FOR_NAME REF_NAME N_COLS TYPE
select * from information_schema.innodb_sys_foreign_cols;
ID FOR_COL_NAME REF_COL_NAME POS
#
# MDEV-29544 SIGSEGV in HA_CREATE_INFO::finalize_locked_tables
#
call mtr.add_suppression("mysql.innodb_index_stats");
set sql_mode= '';
create table t (x int) engine innodb;
insert into t values (77);
alter table mysql.innodb_index_stats modify stat_description char(10);
Warnings:
Warning 1265 Data truncated for column 'stat_description' at row 2
Warning 1265 Data truncated for column 'stat_description' at row 3
lock table t write;
create or replace table t (y int);
ERROR HY000: Error on rename of './test/t' to './test/#sql-backup-t' (errno: 168 "Unknown (generic) error from engine")
unlock tables;
alter table mysql.innodb_index_stats modify stat_description varchar(1024) not null;
select * from t;
x
77
drop table t;
set sql_mode= default;
......@@ -677,3 +677,23 @@ select * from information_schema.innodb_sys_foreign_cols;
drop tables u, t;
select * from information_schema.innodb_sys_foreign;
select * from information_schema.innodb_sys_foreign_cols;
--echo #
--echo # MDEV-29544 SIGSEGV in HA_CREATE_INFO::finalize_locked_tables
--echo #
call mtr.add_suppression("mysql.innodb_index_stats");
set sql_mode= '';
create table t (x int) engine innodb;
insert into t values (77);
alter table mysql.innodb_index_stats modify stat_description char(10);
lock table t write;
--replace_regex /#sql-backup-.+-.+-/#sql-backup-/
--replace_result $MYSQLD_DATADIR ./
--error ER_ERROR_ON_RENAME
create or replace table t (y int);
# cleanup
unlock tables;
alter table mysql.innodb_index_stats modify stat_description varchar(1024) not null;
select * from t;
drop table t;
set sql_mode= default;
......@@ -4398,6 +4398,7 @@ bool HA_CREATE_INFO::finalize_atomic_replace(THD *thd, TABLE_LIST *orig_table)
LEX_CSTRING cpath;
char path[FN_REFLEN + 1];
cpath.str= path;
bool locked_tables_decremented= false;
DBUG_ASSERT(is_atomic_replace());
......@@ -4435,11 +4436,15 @@ bool HA_CREATE_INFO::finalize_atomic_replace(THD *thd, TABLE_LIST *orig_table)
DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db.str,
table_name.str,
MDL_EXCLUSIVE));
/*
HA_EXTRA_PREPARE_FOR_DROP: after CREATE OR REPLACE table
must be not locked, removing it from thd->locked_tables_list.
*/
close_all_tables_for_name(thd, table->s,
HA_EXTRA_PREPARE_FOR_DROP, NULL);
table= NULL;
orig_table->table= NULL;
locked_tables_decremented= true;
}
param.rename_flags= FN_TO_IS_TMP;
......@@ -4450,7 +4455,11 @@ bool HA_CREATE_INFO::finalize_atomic_replace(THD *thd, TABLE_LIST *orig_table)
param.new_alias= backup_name.table_name;
if (rename_table_and_triggers(thd, &param, NULL, orig_table,
&backup_name.db, false, &dummy))
{
if (locked_tables_decremented)
thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables);
return true;
}
debug_crash_here("ddl_log_create_after_save_backup");
}
......@@ -4465,7 +4474,11 @@ bool HA_CREATE_INFO::finalize_atomic_replace(THD *thd, TABLE_LIST *orig_table)
&cpath, &db, &table_name, false) ||
rename_table_and_triggers(thd, &param, NULL, &tmp_name, &db, false,
&dummy))
{
if (locked_tables_decremented)
thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables);
return true;
}
debug_crash_here("ddl_log_create_after_install_new");
return false;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment