Description:
The fix for "Bug#37621360: Importing Table with Instant Drop Column fails" corrects the incorrect mapping of the col pointer for index->fields; however, the name pointer was not addressed.
If you apply the following patch with name pointer checking and run the test case described in the next section, a core dump will occur, revealing the issue as shown below.
```patch
diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc
index 3de5dc19e7c..ee211e476f5 100644
--- a/storage/innobase/row/row0import.cc
+++ b/storage/innobase/row/row0import.cc
@@ -1857,6 +1857,11 @@ dberr_t row_import::add_instant_dropped_columns(dict_table_t *target_table) {
mutex_exit(&dict_sys->mutex);
}
+ for (int i = 0; i < index->n_fields; ++i) {
+ ulint col_nr = dict_col_get_no(index->fields[i].col);
+ const char *col_name = target_table->get_col_name(col_nr);
+ ut_a(col_name == index->fields[i].name());
+ }
return DB_SUCCESS;
}
```
By analyzing the core dump, we can observe that the name string for column o_w_id is located at address 0x7f064c0ca55c, whereas the index points to a stale address 0x7f064c0ca2d4.
```
(gdb) x/15s target_table->col_names
0x7f064c0ca550: "o_id"
0x7f064c0ca555: "o_d_id"
0x7f064c0ca55c: "o_w_id"
0x7f064c0ca563: "o_c_id"
0x7f064c0ca56a: "o_entry_d"
0x7f064c0ca574: "o_carrier_id"
0x7f064c0ca581: "o_ol_cnt"
0x7f064c0ca58a: "o_all_local"
0x7f064c0ca596: "DB_ROW_ID"
0x7f064c0ca5a0: "DB_TRX_ID"
0x7f064c0ca5aa: "DB_ROLL_PTR"
0x7f064c0ca5b6: "!hidden!_dropped_v5_p10_rand_ddl_1111"
0x7f064c0ca5dc: "!hidden!_dropped_v6_p11_rand_ddl_2222"
0x7f064c0ca602: "!hidden!_dropped_v7_p12_rand_ddl_3333"
0x7f064c0ca628: "!hidden!_dropped_v8_p13_rand_ddl_4444"
(gdb) p *index->fields@15
$4 = {{col = 0x7f064c0ca390, name = {m_name = 0x7f064c0ca2d4 "o_w_id"}, prefix_len = 0, fixed_len = 2, is_ascending = 1}, {col = 0x7f064c0ca370, name = {m_name = 0x7f064c0ca2cd "o_d_id"}, prefix_len = 0, fixed_len = 1,
is_ascending = 1}, {col = 0x7f064c0ca350, name = {m_name = 0x7f064c0ca2c8 "o_id"}, prefix_len = 0, fixed_len = 4, is_ascending = 1}, {col = 0x7f064c0ca470, name = {m_name = 0x7f064c0ca318 "DB_TRX_ID"}, prefix_len = 0,
fixed_len = 6, is_ascending = 1}, {col = 0x7f064c0ca490, name = {m_name = 0x7f064c0ca322 "DB_ROLL_PTR"}, prefix_len = 0, fixed_len = 7, is_ascending = 1}, {col = 0x7f064c0ca3b0, name = {m_name = 0x7f064c0ca2db "o_c_id"},
prefix_len = 0, fixed_len = 4, is_ascending = 1}, {col = 0x7f064c0ca3d0, name = {m_name = 0x7f064c0ca2e2 "o_entry_d"}, prefix_len = 0, fixed_len = 5, is_ascending = 1}, {col = 0x7f064c0ca3f0, name = {
m_name = 0x7f064c0ca2ec "o_carrier_id"}, prefix_len = 0, fixed_len = 1, is_ascending = 1}, {col = 0x7f064c0ca410, name = {m_name = 0x7f064c0ca2f9 "o_ol_cnt"}, prefix_len = 0, fixed_len = 1, is_ascending = 1}, {
col = 0x7f064c0ca430, name = {m_name = 0x7f064c0ca302 "o_all_local"}, prefix_len = 0, fixed_len = 1, is_ascending = 1}, {col = 0x7f064c0ca4b0, name = {m_name = 0x7f064c0ca5b6 "!hidden!_dropped_v5_p10_rand_ddl_1111"},
prefix_len = 0, fixed_len = 4, is_ascending = 1}, {col = 0x7f064c0ca4d0, name = {m_name = 0x7f064c0ca5dc "!hidden!_dropped_v6_p11_rand_ddl_2222"}, prefix_len = 0, fixed_len = 4, is_ascending = 1}, {col = 0x7f064c0ca4f0, name = {
m_name = 0x7f064c0ca602 "!hidden!_dropped_v7_p12_rand_ddl_3333"}, prefix_len = 0, fixed_len = 4, is_ascending = 1}, {col = 0x7f064c0ca510, name = {m_name = 0x7f064c0ca628 "!hidden!_dropped_v8_p13_rand_ddl_4444"}, prefix_len = 0,
fixed_len = 4, is_ascending = 1}, {col = 0xdfdfdfdfdfdfdfdf, name = {m_name = 0xdfdfdfdfdfdfdfdf <error: Cannot access memory at address 0xdfdfdfdfdfdfdfdf>}, prefix_len = 4063, fixed_len = 509, is_ascending = 1}}
```
How to repeat:
Testcase as below:
```
let $MYSQLD_DATADIR = `SELECT @@datadir`;
use test;
create table orders (
o_id int not null,
o_d_id tinyint not null,
o_w_id smallint not null,
o_c_id int,
o_entry_d datetime,
o_carrier_id tinyint,
o_ol_cnt tinyint,
o_all_local tinyint,
PRIMARY KEY(o_w_id, o_d_id, o_id)
);
insert into orders values(1, 1, 1, 1, now(), 1, 1, 1);
insert into orders values(2, 1, 1, 1, now(), 1, 1, 1);
insert into orders values(3, 1, 1, 1, now(), 1, 1, 1);
alter table orders add index sec_ind(o_w_id, o_d_id, o_c_id);
alter table orders add index sec_ind2(o_ol_cnt, o_d_id, o_c_id);
alter table orders add index sec_ind3(o_ol_cnt, o_w_id, o_c_id);
alter table orders add column rand_ddl_1111 int, algorithm=instant;
alter table orders add column rand_ddl_2222 int, algorithm=instant;
alter table orders add column rand_ddl_3333 int, algorithm=instant;
alter table orders add column rand_ddl_4444 int, algorithm=instant;
alter table orders drop column rand_ddl_1111, algorithm=instant;
alter table orders drop column rand_ddl_2222, algorithm=instant;
alter table orders drop column rand_ddl_3333, algorithm=instant;
alter table orders drop column rand_ddl_4444, algorithm=instant;
flush table orders for export;
connect (con1,localhost,root,,);
connection con1;
create database test2;
use test2;
CREATE TABLE `orders` (
`o_id` int(11) NOT NULL,
`o_d_id` tinyint(4) NOT NULL,
`o_w_id` smallint(6) NOT NULL,
`o_c_id` int(11) DEFAULT NULL,
`o_entry_d` datetime DEFAULT NULL,
`o_carrier_id` tinyint(4) DEFAULT NULL,
`o_ol_cnt` tinyint(4) DEFAULT NULL,
`o_all_local` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`o_w_id`,`o_d_id`,`o_id`),
KEY `sec_ind` (`o_w_id`,`o_d_id`,`o_c_id`),
KEY `sec_ind2` (`o_ol_cnt`,`o_d_id`,`o_c_id`),
KEY `sec_ind3` (`o_ol_cnt`,`o_w_id`,`o_c_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
alter table orders discard tablespace;
--copy_file $MYSQLD_DATADIR/test/orders.ibd $MYSQLD_DATADIR/test2/orders.ibd
--copy_file $MYSQLD_DATADIR/test/orders.cfg $MYSQLD_DATADIR/test2/orders.cfg
alter table orders import tablespace;
SELECT o_id, o_carrier_id, o_entry_d FROM orders WHERE o_w_id = 2 AND o_d_id = 9 AND o_c_id = 1438 ORDER BY o_id DESC;
connection default;
unlock tables;
connection con1;
drop database test2;
use test;
drop table orders;
disconnect con1;
connection default;
```
Suggested fix:
The column name pointer in the index fields should also be corrected accordingly.
```
diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc
index 3de5dc19e7c..08a0056f0fc 100644
--- a/storage/innobase/row/row0import.cc
+++ b/storage/innobase/row/row0import.cc
@@ -1857,6 +1857,25 @@ dberr_t row_import::add_instant_dropped_columns(dict_table_t *target_table) {
mutex_exit(&dict_sys->mutex);
}
+ /* Update the col->name field with new pointer in target_table. Only the
+ * previous n_fields needed be updated. */
+ for (int32_t i = 0; i < index->n_fields; i++) {
+ ulint col_nr = dict_col_get_no(index->fields[i].col);
+ const char *col_name = target_table->get_col_name(col_nr);
+ index->fields[i].name = col_name;
+ }
+ dict_index_t *sec_index = UT_LIST_GET_FIRST(target_table->indexes);
+ int16_t n_sec_indexes = UT_LIST_GET_LEN(target_table->indexes) - 1;
+ for (int16_t idx = 0; idx < n_sec_indexes; idx++) {
+ sec_index = UT_LIST_GET_NEXT(indexes, sec_index);
+ ut_a(sec_index);
+ for (int32_t i = 0; i < sec_index->n_fields; ++i) {
+ ulint col_nr = dict_col_get_no(sec_index->fields[i].col);
+ const char *col_name = target_table->get_col_name(col_nr);
+ sec_index->fields[i].name = col_name;
+ }
+ }
+
return DB_SUCCESS;
}
```
Description: The fix for "Bug#37621360: Importing Table with Instant Drop Column fails" corrects the incorrect mapping of the col pointer for index->fields; however, the name pointer was not addressed. If you apply the following patch with name pointer checking and run the test case described in the next section, a core dump will occur, revealing the issue as shown below. ```patch diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index 3de5dc19e7c..ee211e476f5 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -1857,6 +1857,11 @@ dberr_t row_import::add_instant_dropped_columns(dict_table_t *target_table) { mutex_exit(&dict_sys->mutex); } + for (int i = 0; i < index->n_fields; ++i) { + ulint col_nr = dict_col_get_no(index->fields[i].col); + const char *col_name = target_table->get_col_name(col_nr); + ut_a(col_name == index->fields[i].name()); + } return DB_SUCCESS; } ``` By analyzing the core dump, we can observe that the name string for column o_w_id is located at address 0x7f064c0ca55c, whereas the index points to a stale address 0x7f064c0ca2d4. ``` (gdb) x/15s target_table->col_names 0x7f064c0ca550: "o_id" 0x7f064c0ca555: "o_d_id" 0x7f064c0ca55c: "o_w_id" 0x7f064c0ca563: "o_c_id" 0x7f064c0ca56a: "o_entry_d" 0x7f064c0ca574: "o_carrier_id" 0x7f064c0ca581: "o_ol_cnt" 0x7f064c0ca58a: "o_all_local" 0x7f064c0ca596: "DB_ROW_ID" 0x7f064c0ca5a0: "DB_TRX_ID" 0x7f064c0ca5aa: "DB_ROLL_PTR" 0x7f064c0ca5b6: "!hidden!_dropped_v5_p10_rand_ddl_1111" 0x7f064c0ca5dc: "!hidden!_dropped_v6_p11_rand_ddl_2222" 0x7f064c0ca602: "!hidden!_dropped_v7_p12_rand_ddl_3333" 0x7f064c0ca628: "!hidden!_dropped_v8_p13_rand_ddl_4444" (gdb) p *index->fields@15 $4 = {{col = 0x7f064c0ca390, name = {m_name = 0x7f064c0ca2d4 "o_w_id"}, prefix_len = 0, fixed_len = 2, is_ascending = 1}, {col = 0x7f064c0ca370, name = {m_name = 0x7f064c0ca2cd "o_d_id"}, prefix_len = 0, fixed_len = 1, is_ascending = 1}, {col = 0x7f064c0ca350, name = {m_name = 0x7f064c0ca2c8 "o_id"}, prefix_len = 0, fixed_len = 4, is_ascending = 1}, {col = 0x7f064c0ca470, name = {m_name = 0x7f064c0ca318 "DB_TRX_ID"}, prefix_len = 0, fixed_len = 6, is_ascending = 1}, {col = 0x7f064c0ca490, name = {m_name = 0x7f064c0ca322 "DB_ROLL_PTR"}, prefix_len = 0, fixed_len = 7, is_ascending = 1}, {col = 0x7f064c0ca3b0, name = {m_name = 0x7f064c0ca2db "o_c_id"}, prefix_len = 0, fixed_len = 4, is_ascending = 1}, {col = 0x7f064c0ca3d0, name = {m_name = 0x7f064c0ca2e2 "o_entry_d"}, prefix_len = 0, fixed_len = 5, is_ascending = 1}, {col = 0x7f064c0ca3f0, name = { m_name = 0x7f064c0ca2ec "o_carrier_id"}, prefix_len = 0, fixed_len = 1, is_ascending = 1}, {col = 0x7f064c0ca410, name = {m_name = 0x7f064c0ca2f9 "o_ol_cnt"}, prefix_len = 0, fixed_len = 1, is_ascending = 1}, { col = 0x7f064c0ca430, name = {m_name = 0x7f064c0ca302 "o_all_local"}, prefix_len = 0, fixed_len = 1, is_ascending = 1}, {col = 0x7f064c0ca4b0, name = {m_name = 0x7f064c0ca5b6 "!hidden!_dropped_v5_p10_rand_ddl_1111"}, prefix_len = 0, fixed_len = 4, is_ascending = 1}, {col = 0x7f064c0ca4d0, name = {m_name = 0x7f064c0ca5dc "!hidden!_dropped_v6_p11_rand_ddl_2222"}, prefix_len = 0, fixed_len = 4, is_ascending = 1}, {col = 0x7f064c0ca4f0, name = { m_name = 0x7f064c0ca602 "!hidden!_dropped_v7_p12_rand_ddl_3333"}, prefix_len = 0, fixed_len = 4, is_ascending = 1}, {col = 0x7f064c0ca510, name = {m_name = 0x7f064c0ca628 "!hidden!_dropped_v8_p13_rand_ddl_4444"}, prefix_len = 0, fixed_len = 4, is_ascending = 1}, {col = 0xdfdfdfdfdfdfdfdf, name = {m_name = 0xdfdfdfdfdfdfdfdf <error: Cannot access memory at address 0xdfdfdfdfdfdfdfdf>}, prefix_len = 4063, fixed_len = 509, is_ascending = 1}} ``` How to repeat: Testcase as below: ``` let $MYSQLD_DATADIR = `SELECT @@datadir`; use test; create table orders ( o_id int not null, o_d_id tinyint not null, o_w_id smallint not null, o_c_id int, o_entry_d datetime, o_carrier_id tinyint, o_ol_cnt tinyint, o_all_local tinyint, PRIMARY KEY(o_w_id, o_d_id, o_id) ); insert into orders values(1, 1, 1, 1, now(), 1, 1, 1); insert into orders values(2, 1, 1, 1, now(), 1, 1, 1); insert into orders values(3, 1, 1, 1, now(), 1, 1, 1); alter table orders add index sec_ind(o_w_id, o_d_id, o_c_id); alter table orders add index sec_ind2(o_ol_cnt, o_d_id, o_c_id); alter table orders add index sec_ind3(o_ol_cnt, o_w_id, o_c_id); alter table orders add column rand_ddl_1111 int, algorithm=instant; alter table orders add column rand_ddl_2222 int, algorithm=instant; alter table orders add column rand_ddl_3333 int, algorithm=instant; alter table orders add column rand_ddl_4444 int, algorithm=instant; alter table orders drop column rand_ddl_1111, algorithm=instant; alter table orders drop column rand_ddl_2222, algorithm=instant; alter table orders drop column rand_ddl_3333, algorithm=instant; alter table orders drop column rand_ddl_4444, algorithm=instant; flush table orders for export; connect (con1,localhost,root,,); connection con1; create database test2; use test2; CREATE TABLE `orders` ( `o_id` int(11) NOT NULL, `o_d_id` tinyint(4) NOT NULL, `o_w_id` smallint(6) NOT NULL, `o_c_id` int(11) DEFAULT NULL, `o_entry_d` datetime DEFAULT NULL, `o_carrier_id` tinyint(4) DEFAULT NULL, `o_ol_cnt` tinyint(4) DEFAULT NULL, `o_all_local` tinyint(4) DEFAULT NULL, PRIMARY KEY (`o_w_id`,`o_d_id`,`o_id`), KEY `sec_ind` (`o_w_id`,`o_d_id`,`o_c_id`), KEY `sec_ind2` (`o_ol_cnt`,`o_d_id`,`o_c_id`), KEY `sec_ind3` (`o_ol_cnt`,`o_w_id`,`o_c_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; alter table orders discard tablespace; --copy_file $MYSQLD_DATADIR/test/orders.ibd $MYSQLD_DATADIR/test2/orders.ibd --copy_file $MYSQLD_DATADIR/test/orders.cfg $MYSQLD_DATADIR/test2/orders.cfg alter table orders import tablespace; SELECT o_id, o_carrier_id, o_entry_d FROM orders WHERE o_w_id = 2 AND o_d_id = 9 AND o_c_id = 1438 ORDER BY o_id DESC; connection default; unlock tables; connection con1; drop database test2; use test; drop table orders; disconnect con1; connection default; ``` Suggested fix: The column name pointer in the index fields should also be corrected accordingly. ``` diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index 3de5dc19e7c..08a0056f0fc 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -1857,6 +1857,25 @@ dberr_t row_import::add_instant_dropped_columns(dict_table_t *target_table) { mutex_exit(&dict_sys->mutex); } + /* Update the col->name field with new pointer in target_table. Only the + * previous n_fields needed be updated. */ + for (int32_t i = 0; i < index->n_fields; i++) { + ulint col_nr = dict_col_get_no(index->fields[i].col); + const char *col_name = target_table->get_col_name(col_nr); + index->fields[i].name = col_name; + } + dict_index_t *sec_index = UT_LIST_GET_FIRST(target_table->indexes); + int16_t n_sec_indexes = UT_LIST_GET_LEN(target_table->indexes) - 1; + for (int16_t idx = 0; idx < n_sec_indexes; idx++) { + sec_index = UT_LIST_GET_NEXT(indexes, sec_index); + ut_a(sec_index); + for (int32_t i = 0; i < sec_index->n_fields; ++i) { + ulint col_nr = dict_col_get_no(sec_index->fields[i].col); + const char *col_name = target_table->get_col_name(col_nr); + sec_index->fields[i].name = col_name; + } + } + return DB_SUCCESS; } ```