Description:
Bug: UAF in dd_open_fk_tables when a table is evicted from dict cache.
I run test case in 8.0.45, but the same issue persists in the latest version.
while its heap still contains FK child table names stored in fk_list.
when two operations are replayed concurrently:
- Thread 1 opens table ta and traverses
its FK chain via dd_open_fk_tables. Inside dd_table_check_for_child,
child-table names are allocated in the *parent* table's heap and
pushed into fk_list (dict_names_t). After tc is opened and closed
(ref_count -> 0), td's name pointer still lives inside tc->heap.
- Thread 2 which evicts table tc and frees tc->heap.
Thread 1 then dereferences the now-dangling td name pointer -> UAF.
Error message like this:
MySQL Version 8.0.45
==35954==ERROR: AddressSanitizer: heap-use-after-free on address 0x61d000624ae0 at pc 0x7fa670e913be bp 0x7fa64b47b2e0 sp 0x7fa64b47aa88
READ of size 8 at 0x61d000624ae0 thread T47 (connection)
#0 0x7fa670e913bd (/lib64/libasan.so.4+0x513bd)
#1 0x7fa66f277be8 in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) (/lib64/libstdc++.so.6+0x96be8)
#2 0x5c556f9 in ib::logger& ib::logger::operator<< <char*>(char* const&) /disk2/myx/mysql-server/storage/innobase/include/ut0log.h:70
#3 0x6426b2f in dd_open_fk_tables(std::deque<char const*, ut::allocator<char const*, ut::detail::allocator_base_pfs<char const*> > >&, bool, THD*) /disk2/myx/mysql-server/storage/innobase/dict/dict0dd.cc:5394
#4 0x64539b4 in dict_table_t* dd_open_table<dd::Table>(dd::cache::Dictionary_client*, TABLE const*, char const*, dd::Table const*, THD*) /disk2/myx/mysql-server/storage/innobase/dict/dict0dd.cc:5422
#5 0x5c33461 in ha_innobase::open(char const*, int, unsigned int, dd::Table const*) /disk2/myx/mysql-server/storage/innobase/handler/ha_innodb.cc:7289
#6 0x3a43627 in handler::ha_open(TABLE*, char const*, int, int, dd::Table const*) /disk2/myx/mysql-server/sql/handler.cc:2790
#7 0x368a156 in open_table_from_share(THD*, TABLE_SHARE*, char const*, unsigned int, unsigned int, unsigned int, TABLE*, bool, dd::Table const*) /disk2/myx/mysql-server/sql/table.cc:3212
#8 0x31da12f in open_table(THD*, Table_ref*, Open_table_context*) /disk2/myx/mysql-server/sql/sql_base.cc:3386
#9 0x31db355 in open_and_process_table /disk2/myx/mysql-server/sql/sql_base.cc:5053
#10 0x31dc0a9 in open_tables(THD*, Table_ref**, unsigned int*, unsigned int, Prelocking_strategy*) /disk2/myx/mysql-server/sql/sql_base.cc:5858
#11 0x31df022 in open_tables_for_query(THD*, Table_ref*, unsigned int) /disk2/myx/mysql-server/sql/sql_base.cc:6747
0x61d000624ae0 is located 608 bytes inside of 1960-byte region [0x61d000624880,0x61d000625028)
freed by thread T48 (connection) here:
#0 0x7fa670f1e508 in __interceptor_free (/lib64/libasan.so.4+0xde508)
#1 0x5c4ca5d in ut::detail::free(void*) /disk2/myx/mysql-server/storage/innobase/include/detail/ut/allocator_traits.h:77
#2 0x5c4ca89 in ut::detail::Alloc_fn::free(void*) /disk2/myx/mysql-server/storage/innobase/include/detail/ut/allocator_traits.h:105
#3 0x5c4cd8e in ut::detail::Alloc_pfs::free(void*) /disk2/myx/mysql-server/storage/innobase/include/detail/ut/alloc.h:381
#4 0x5c53c3d in ut::detail::Alloc_<ut::detail::Alloc_pfs>::free(void*) /disk2/myx/mysql-server/storage/innobase/include/detail/ut/alloc.h:456
#5 0x5c53c48 in ut::free(void*) /disk2/myx/mysql-server/storage/innobase/include/ut0new.h:721
#6 0x61c272e in mem_heap_block_free(mem_block_info_t*, mem_block_info_t*) /disk2/myx/mysql-server/storage/innobase/mem/memory.cc:440
#7 0x61bf4d8 in mem_heap_free /disk2/myx/mysql-server/storage/innobase/include/mem0mem.ic:465
#8 0x61bf942 in dict_mem_table_free(dict_table_t*) /disk2/myx/mysql-server/storage/innobase/dict/mem.cc:147
#9 0x63e4224 in dict_table_remove_from_cache_low /disk2/myx/mysql-server/storage/innobase/dict/dict0dict.cc:1970
#10 0x63e4b67 in dict_table_remove_from_cache_debug(dict_table_t*, bool) /disk2/myx/mysql-server/storage/innobase/dict/dict0dict.cc:2022
#11 0x5c3b83b in innodb_internal_table_validate /disk2/myx/mysql-server/storage/innobase/handler/ha_innodb.cc:20659
#12 0x340b8b0 in sys_var_pluginvar::do_check(THD*, set_var*) /disk2/myx/mysql-server/sql/sql_plugin_var.cc:333
How to repeat:
mtr case:
CREATE TABLE ta (id INT PRIMARY KEY) ENGINE = InnoDB;
CREATE TABLE tb (
id INT PRIMARY KEY,
aid INT,
CONSTRAINT fk_tb_ta FOREIGN KEY (aid) REFERENCES ta (id)
) ENGINE = InnoDB;
CREATE TABLE tc (
id INT PRIMARY KEY,
bid INT,
CONSTRAINT fk_tc_tb FOREIGN KEY (bid) REFERENCES tb (id)
) ENGINE = InnoDB;
CREATE TABLE td (
id INT PRIMARY KEY,
cid INT,
CONSTRAINT fk_td_tc FOREIGN KEY (cid) REFERENCES tc (id)
) ENGINE = InnoDB;
--echo #evict all table cache
SET GLOBAL debug="+d,innodb_evict_autoinc_table";
--echo # Evict table from dictionary cache
--error ER_WRONG_VALUE_FOR_VAR
SET GLOBAL innodb_ft_aux_table="test/ta";
--error ER_WRONG_VALUE_FOR_VAR
SET GLOBAL innodb_ft_aux_table="test/tb";
--error ER_WRONG_VALUE_FOR_VAR
SET GLOBAL innodb_ft_aux_table="test/tc";
--error ER_WRONG_VALUE_FOR_VAR
SET GLOBAL innodb_ft_aux_table="test/td";
SET GLOBAL debug="-d,innodb_evict_autoinc_table";
--send select * from ta
--connect (con1,localhost,root,,)
--connection con1
SET GLOBAL DEBUG = '+d,dd_open_fk_tables_sync';
--echo # --- iteration 1 (tb) ---
SET DEBUG_SYNC = 'now WAIT_FOR fk_at_sync';
SET DEBUG_SYNC = 'now SIGNAL fk_continue';
--echo # --- iteration 2 (tc) ---
SET DEBUG_SYNC = 'now WAIT_FOR fk_at_sync';
SET DEBUG_SYNC = 'now SIGNAL fk_continue';
--echo # --- iteration 3 (td) ---
# con1 holds the td name pointer (inside tc->heap) but has not yet
# called dd_open_table_one_on_name for td.
# Without the fix the td name pointer becomes dangling.
SET DEBUG_SYNC = 'now WAIT_FOR fk_at_sync';
--echo #evict tc table cache
SET GLOBAL debug="+d,innodb_evict_autoinc_table";
--error ER_WRONG_VALUE_FOR_VAR
SET GLOBAL innodb_ft_aux_table="test/tc";
SET GLOBAL debug="-d,innodb_evict_autoinc_table";
SET DEBUG_SYNC = 'now SIGNAL fk_continue';
# With the bug this crashes or triggers
# an ASAN heap-use-after-free; with the fix it succeeds.
connection default;
--reap
SET GLOBAL DEBUG = '-d,dd_open_fk_tables_sync';
DROP TABLE td;
DROP TABLE tc;
DROP TABLE tb;
DROP TABLE ta;
disconnect con1;
Suggested fix:
diff --git a/storage/innobase/dict/dict0dd.cc b/storage/innobase/dict/dict0dd.cc
index b87e4963e51..4dd504a530b 100644
--- a/storage/innobase/dict/dict0dd.cc
+++ b/storage/innobase/dict/dict0dd.cc
@@ -4572,13 +4572,7 @@ dberr_t dd_table_check_for_child(dd::cache::Dictionary_client *client,
the child table name is saved here. The child
table will be loaded later, along with its
foreign key constraint. */
- lint old_size = mem_heap_get_size(m_table->heap);
-
- fk_tables->push_back(
- mem_heap_strdupl(m_table->heap, full_name, strlen(full_name)));
-
- lint new_size = mem_heap_get_size(m_table->heap);
- dict_sys->size += new_size - old_size;
+ fk_tables->emplace_back(full_name);
}
dict_sys_mutex_exit();
@@ -5374,19 +5368,24 @@ func_exit:
@param[in] thd thread THD */
void dd_open_fk_tables(dict_names_t &fk_list, bool dict_locked, THD *thd) {
while (!fk_list.empty()) {
- char *name = const_cast<char *>(fk_list.front());
+ std::string name = std::move(fk_list.front());
if (innobase_get_lower_case_table_names() == 2) {
- innobase_casedn_str(name);
+ innobase_casedn_str(&name[0]);
} else {
#ifndef _WIN32
if (innobase_get_lower_case_table_names() == 1) {
- innobase_casedn_str(name);
+ innobase_casedn_str(&name[0]);
}
#endif /* !_WIN32 */
}
- dd_open_table_one_on_name(name, dict_locked, fk_list, thd);
+ DBUG_EXECUTE_IF("dd_open_fk_tables_sync", {
+ const char act[] = "now SIGNAL fk_at_sync WAIT_FOR fk_continue";
+ assert(!debug_sync_set_action(thd, STRING_WITH_LEN(act)));
+ });
+
+ dd_open_table_one_on_name(name.c_str(), dict_locked, fk_list, thd);
fk_list.pop_front();
}
diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc
index bec321164a4..a407013e8e1 100644
--- a/storage/innobase/dict/dict0load.cc
+++ b/storage/innobase/dict/dict0load.cc
@@ -2182,7 +2182,6 @@ dict_table_t *dict_load_table(const char *name, bool cached,
dict_err_ignore_t ignore_err,
const std::string *prev_table) {
dict_names_t fk_list;
- dict_names_t::iterator i;
table_name_t table_name;
dict_table_t *result;
@@ -2207,8 +2206,8 @@ dict_table_t *dict_load_table(const char *name, bool cached,
while (!fk_list.empty()) {
table_name_t fk_table_name;
dict_table_t *fk_table;
-
- fk_table_name.m_name = const_cast<char *>(fk_list.front());
+ std::string fk_name = std::move(fk_list.front());
+ fk_table_name.m_name = const_cast<char *>(fk_name.c_str());
fk_table = dict_table_check_if_in_cache_low(fk_table_name.m_name);
if (!fk_table) {
dict_load_table_one(fk_table_name, cached, ignore_err, fk_list,
@@ -2876,16 +2875,9 @@ stack. */
here. The child table will be loaded later, along with its
foreign key constraint. */
- lint old_size = mem_heap_get_size(ref_table->heap);
-
ut_a(ref_table != nullptr);
- fk_tables.push_back(mem_heap_strdupl(ref_table->heap,
- foreign->foreign_table_name_lookup,
- foreign_table_name_len));
-
- lint new_size = mem_heap_get_size(ref_table->heap);
- dict_sys->size += new_size - old_size;
-
+ fk_tables.emplace_back(foreign->foreign_table_name_lookup,
+ foreign_table_name_len);
dict_foreign_remove_from_cache(foreign);
return DB_SUCCESS;
}
diff --git a/storage/innobase/include/dict0load.h b/storage/innobase/include/dict0load.h
index 533825ff8dd..631f5279e52 100644
--- a/storage/innobase/include/dict0load.h
+++ b/storage/innobase/include/dict0load.h
@@ -47,7 +47,7 @@ this program; if not, write to the Free Software Foundation, Inc.,
#include <deque>
/** A stack of table names related through foreign key constraints */
-typedef std::deque<const char *, ut::allocator<const char *>> dict_names_t;
+typedef std::deque<std::string> dict_names_t;
/** enum that defines all system table IDs. @see SYSTEM_TABLE_NAME[] */
enum dict_system_id_t {
diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc
index 919d2db1d7a..cec37b68224 100644
--- a/storage/innobase/row/row0mysql.cc
+++ b/storage/innobase/row/row0mysql.cc
@@ -4292,7 +4292,7 @@ dberr_t row_rename_table_for_mysql(const char *old_name, const char *new_name,
if (is_rename) {
/* Ensure that old renamed table names are not in this list. */
for (auto it = fk_tables.begin(); it != fk_tables.end();) {
- if (strcmp(*it, old_name) == 0) {
+ if (*it == old_name) {
it = fk_tables.erase(it);
} else {
++it;
Description: Bug: UAF in dd_open_fk_tables when a table is evicted from dict cache. I run test case in 8.0.45, but the same issue persists in the latest version. while its heap still contains FK child table names stored in fk_list. when two operations are replayed concurrently: - Thread 1 opens table ta and traverses its FK chain via dd_open_fk_tables. Inside dd_table_check_for_child, child-table names are allocated in the *parent* table's heap and pushed into fk_list (dict_names_t). After tc is opened and closed (ref_count -> 0), td's name pointer still lives inside tc->heap. - Thread 2 which evicts table tc and frees tc->heap. Thread 1 then dereferences the now-dangling td name pointer -> UAF. Error message like this: MySQL Version 8.0.45 ==35954==ERROR: AddressSanitizer: heap-use-after-free on address 0x61d000624ae0 at pc 0x7fa670e913be bp 0x7fa64b47b2e0 sp 0x7fa64b47aa88 READ of size 8 at 0x61d000624ae0 thread T47 (connection) #0 0x7fa670e913bd (/lib64/libasan.so.4+0x513bd) #1 0x7fa66f277be8 in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) (/lib64/libstdc++.so.6+0x96be8) #2 0x5c556f9 in ib::logger& ib::logger::operator<< <char*>(char* const&) /disk2/myx/mysql-server/storage/innobase/include/ut0log.h:70 #3 0x6426b2f in dd_open_fk_tables(std::deque<char const*, ut::allocator<char const*, ut::detail::allocator_base_pfs<char const*> > >&, bool, THD*) /disk2/myx/mysql-server/storage/innobase/dict/dict0dd.cc:5394 #4 0x64539b4 in dict_table_t* dd_open_table<dd::Table>(dd::cache::Dictionary_client*, TABLE const*, char const*, dd::Table const*, THD*) /disk2/myx/mysql-server/storage/innobase/dict/dict0dd.cc:5422 #5 0x5c33461 in ha_innobase::open(char const*, int, unsigned int, dd::Table const*) /disk2/myx/mysql-server/storage/innobase/handler/ha_innodb.cc:7289 #6 0x3a43627 in handler::ha_open(TABLE*, char const*, int, int, dd::Table const*) /disk2/myx/mysql-server/sql/handler.cc:2790 #7 0x368a156 in open_table_from_share(THD*, TABLE_SHARE*, char const*, unsigned int, unsigned int, unsigned int, TABLE*, bool, dd::Table const*) /disk2/myx/mysql-server/sql/table.cc:3212 #8 0x31da12f in open_table(THD*, Table_ref*, Open_table_context*) /disk2/myx/mysql-server/sql/sql_base.cc:3386 #9 0x31db355 in open_and_process_table /disk2/myx/mysql-server/sql/sql_base.cc:5053 #10 0x31dc0a9 in open_tables(THD*, Table_ref**, unsigned int*, unsigned int, Prelocking_strategy*) /disk2/myx/mysql-server/sql/sql_base.cc:5858 #11 0x31df022 in open_tables_for_query(THD*, Table_ref*, unsigned int) /disk2/myx/mysql-server/sql/sql_base.cc:6747 0x61d000624ae0 is located 608 bytes inside of 1960-byte region [0x61d000624880,0x61d000625028) freed by thread T48 (connection) here: #0 0x7fa670f1e508 in __interceptor_free (/lib64/libasan.so.4+0xde508) #1 0x5c4ca5d in ut::detail::free(void*) /disk2/myx/mysql-server/storage/innobase/include/detail/ut/allocator_traits.h:77 #2 0x5c4ca89 in ut::detail::Alloc_fn::free(void*) /disk2/myx/mysql-server/storage/innobase/include/detail/ut/allocator_traits.h:105 #3 0x5c4cd8e in ut::detail::Alloc_pfs::free(void*) /disk2/myx/mysql-server/storage/innobase/include/detail/ut/alloc.h:381 #4 0x5c53c3d in ut::detail::Alloc_<ut::detail::Alloc_pfs>::free(void*) /disk2/myx/mysql-server/storage/innobase/include/detail/ut/alloc.h:456 #5 0x5c53c48 in ut::free(void*) /disk2/myx/mysql-server/storage/innobase/include/ut0new.h:721 #6 0x61c272e in mem_heap_block_free(mem_block_info_t*, mem_block_info_t*) /disk2/myx/mysql-server/storage/innobase/mem/memory.cc:440 #7 0x61bf4d8 in mem_heap_free /disk2/myx/mysql-server/storage/innobase/include/mem0mem.ic:465 #8 0x61bf942 in dict_mem_table_free(dict_table_t*) /disk2/myx/mysql-server/storage/innobase/dict/mem.cc:147 #9 0x63e4224 in dict_table_remove_from_cache_low /disk2/myx/mysql-server/storage/innobase/dict/dict0dict.cc:1970 #10 0x63e4b67 in dict_table_remove_from_cache_debug(dict_table_t*, bool) /disk2/myx/mysql-server/storage/innobase/dict/dict0dict.cc:2022 #11 0x5c3b83b in innodb_internal_table_validate /disk2/myx/mysql-server/storage/innobase/handler/ha_innodb.cc:20659 #12 0x340b8b0 in sys_var_pluginvar::do_check(THD*, set_var*) /disk2/myx/mysql-server/sql/sql_plugin_var.cc:333 How to repeat: mtr case: CREATE TABLE ta (id INT PRIMARY KEY) ENGINE = InnoDB; CREATE TABLE tb ( id INT PRIMARY KEY, aid INT, CONSTRAINT fk_tb_ta FOREIGN KEY (aid) REFERENCES ta (id) ) ENGINE = InnoDB; CREATE TABLE tc ( id INT PRIMARY KEY, bid INT, CONSTRAINT fk_tc_tb FOREIGN KEY (bid) REFERENCES tb (id) ) ENGINE = InnoDB; CREATE TABLE td ( id INT PRIMARY KEY, cid INT, CONSTRAINT fk_td_tc FOREIGN KEY (cid) REFERENCES tc (id) ) ENGINE = InnoDB; --echo #evict all table cache SET GLOBAL debug="+d,innodb_evict_autoinc_table"; --echo # Evict table from dictionary cache --error ER_WRONG_VALUE_FOR_VAR SET GLOBAL innodb_ft_aux_table="test/ta"; --error ER_WRONG_VALUE_FOR_VAR SET GLOBAL innodb_ft_aux_table="test/tb"; --error ER_WRONG_VALUE_FOR_VAR SET GLOBAL innodb_ft_aux_table="test/tc"; --error ER_WRONG_VALUE_FOR_VAR SET GLOBAL innodb_ft_aux_table="test/td"; SET GLOBAL debug="-d,innodb_evict_autoinc_table"; --send select * from ta --connect (con1,localhost,root,,) --connection con1 SET GLOBAL DEBUG = '+d,dd_open_fk_tables_sync'; --echo # --- iteration 1 (tb) --- SET DEBUG_SYNC = 'now WAIT_FOR fk_at_sync'; SET DEBUG_SYNC = 'now SIGNAL fk_continue'; --echo # --- iteration 2 (tc) --- SET DEBUG_SYNC = 'now WAIT_FOR fk_at_sync'; SET DEBUG_SYNC = 'now SIGNAL fk_continue'; --echo # --- iteration 3 (td) --- # con1 holds the td name pointer (inside tc->heap) but has not yet # called dd_open_table_one_on_name for td. # Without the fix the td name pointer becomes dangling. SET DEBUG_SYNC = 'now WAIT_FOR fk_at_sync'; --echo #evict tc table cache SET GLOBAL debug="+d,innodb_evict_autoinc_table"; --error ER_WRONG_VALUE_FOR_VAR SET GLOBAL innodb_ft_aux_table="test/tc"; SET GLOBAL debug="-d,innodb_evict_autoinc_table"; SET DEBUG_SYNC = 'now SIGNAL fk_continue'; # With the bug this crashes or triggers # an ASAN heap-use-after-free; with the fix it succeeds. connection default; --reap SET GLOBAL DEBUG = '-d,dd_open_fk_tables_sync'; DROP TABLE td; DROP TABLE tc; DROP TABLE tb; DROP TABLE ta; disconnect con1; Suggested fix: diff --git a/storage/innobase/dict/dict0dd.cc b/storage/innobase/dict/dict0dd.cc index b87e4963e51..4dd504a530b 100644 --- a/storage/innobase/dict/dict0dd.cc +++ b/storage/innobase/dict/dict0dd.cc @@ -4572,13 +4572,7 @@ dberr_t dd_table_check_for_child(dd::cache::Dictionary_client *client, the child table name is saved here. The child table will be loaded later, along with its foreign key constraint. */ - lint old_size = mem_heap_get_size(m_table->heap); - - fk_tables->push_back( - mem_heap_strdupl(m_table->heap, full_name, strlen(full_name))); - - lint new_size = mem_heap_get_size(m_table->heap); - dict_sys->size += new_size - old_size; + fk_tables->emplace_back(full_name); } dict_sys_mutex_exit(); @@ -5374,19 +5368,24 @@ func_exit: @param[in] thd thread THD */ void dd_open_fk_tables(dict_names_t &fk_list, bool dict_locked, THD *thd) { while (!fk_list.empty()) { - char *name = const_cast<char *>(fk_list.front()); + std::string name = std::move(fk_list.front()); if (innobase_get_lower_case_table_names() == 2) { - innobase_casedn_str(name); + innobase_casedn_str(&name[0]); } else { #ifndef _WIN32 if (innobase_get_lower_case_table_names() == 1) { - innobase_casedn_str(name); + innobase_casedn_str(&name[0]); } #endif /* !_WIN32 */ } - dd_open_table_one_on_name(name, dict_locked, fk_list, thd); + DBUG_EXECUTE_IF("dd_open_fk_tables_sync", { + const char act[] = "now SIGNAL fk_at_sync WAIT_FOR fk_continue"; + assert(!debug_sync_set_action(thd, STRING_WITH_LEN(act))); + }); + + dd_open_table_one_on_name(name.c_str(), dict_locked, fk_list, thd); fk_list.pop_front(); } diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index bec321164a4..a407013e8e1 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -2182,7 +2182,6 @@ dict_table_t *dict_load_table(const char *name, bool cached, dict_err_ignore_t ignore_err, const std::string *prev_table) { dict_names_t fk_list; - dict_names_t::iterator i; table_name_t table_name; dict_table_t *result; @@ -2207,8 +2206,8 @@ dict_table_t *dict_load_table(const char *name, bool cached, while (!fk_list.empty()) { table_name_t fk_table_name; dict_table_t *fk_table; - - fk_table_name.m_name = const_cast<char *>(fk_list.front()); + std::string fk_name = std::move(fk_list.front()); + fk_table_name.m_name = const_cast<char *>(fk_name.c_str()); fk_table = dict_table_check_if_in_cache_low(fk_table_name.m_name); if (!fk_table) { dict_load_table_one(fk_table_name, cached, ignore_err, fk_list, @@ -2876,16 +2875,9 @@ stack. */ here. The child table will be loaded later, along with its foreign key constraint. */ - lint old_size = mem_heap_get_size(ref_table->heap); - ut_a(ref_table != nullptr); - fk_tables.push_back(mem_heap_strdupl(ref_table->heap, - foreign->foreign_table_name_lookup, - foreign_table_name_len)); - - lint new_size = mem_heap_get_size(ref_table->heap); - dict_sys->size += new_size - old_size; - + fk_tables.emplace_back(foreign->foreign_table_name_lookup, + foreign_table_name_len); dict_foreign_remove_from_cache(foreign); return DB_SUCCESS; } diff --git a/storage/innobase/include/dict0load.h b/storage/innobase/include/dict0load.h index 533825ff8dd..631f5279e52 100644 --- a/storage/innobase/include/dict0load.h +++ b/storage/innobase/include/dict0load.h @@ -47,7 +47,7 @@ this program; if not, write to the Free Software Foundation, Inc., #include <deque> /** A stack of table names related through foreign key constraints */ -typedef std::deque<const char *, ut::allocator<const char *>> dict_names_t; +typedef std::deque<std::string> dict_names_t; /** enum that defines all system table IDs. @see SYSTEM_TABLE_NAME[] */ enum dict_system_id_t { diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 919d2db1d7a..cec37b68224 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -4292,7 +4292,7 @@ dberr_t row_rename_table_for_mysql(const char *old_name, const char *new_name, if (is_rename) { /* Ensure that old renamed table names are not in this list. */ for (auto it = fk_tables.begin(); it != fk_tables.end();) { - if (strcmp(*it, old_name) == 0) { + if (*it == old_name) { it = fk_tables.erase(it); } else { ++it;