Bug #119517 Importing Table with Instant Drop Column not fix index col name pointer
Submitted: 5 Dec 15:25 Modified: 15 Dec 5:26
Reporter: Chong Lee (OCA) Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S3 (Non-critical)
Version:8.0.44 OS:Any
Assigned to: CPU Architecture:Any

[5 Dec 15:25] Chong Lee
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;
 }
```
[10 Dec 7:55] yifei syc
Hello Mr. Lee,I'm trying to fully grasp the impact of the pointer mismatch.

The GDB output shows two different pointers for the same conceptual column name. Assuming the string content at both locations is the same, what is the specific risk of leaving these pointers inconsistent? Would this pointer discrepancy cause any functional problems or lead to memory-related bugs later on?
[10 Dec 7:55] yifei syc
Hello Mr. Lee,I'm trying to fully grasp the impact of the pointer mismatch.

The GDB output shows two different pointers for the same conceptual column name. Assuming the string content at both locations is the same, what is the specific risk of leaving these pointers inconsistent? Would this pointer discrepancy cause any functional problems or lead to memory-related bugs later on?
[15 Dec 5:26] Chong Lee
Currently, this does not cause any issues because `col_names` are allocated on the table's heap, the old values will not be reused or overwritten. Nevertheless, this constitutes a defect that could potentially cause problems in future development.