From f6e00abda0c786d1834eb1b5da81f05211ec3769 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 28 Jan 2025 11:56:35 +0530 Subject: [PATCH] MDEV-35169 ALTER TABLE...IMPORT TABLESPACE does not work with INDEX DESC Problem: ======= - Import tablespace fails to check the index fields descending property while matching the schema given in cfg file with the table schema. Fix: === row_quiesce_write_index_fields(): Write the descending property of the field into field fixed length field. Since the field fixed length uses only 10 bits, InnoDB can use 0th bit of the field fixed length to store the descending field property. row_import_cfg_read_index_fields(): Read the field descending information from field fixed length. --- mysql-test/suite/innodb/r/import_cfg.result | 67 +++++++++++++++ .../suite/innodb/r/innodb-wl5522-debug.result | 10 --- mysql-test/suite/innodb/t/import_cfg.test | 83 +++++++++++++++++++ .../suite/innodb/t/innodb-wl5522-debug.test | 16 ---- storage/innobase/row/row0import.cc | 16 +++- storage/innobase/row/row0quiesce.cc | 33 +++----- 6 files changed, 178 insertions(+), 47 deletions(-) create mode 100644 mysql-test/suite/innodb/r/import_cfg.result create mode 100644 mysql-test/suite/innodb/t/import_cfg.test diff --git a/mysql-test/suite/innodb/r/import_cfg.result b/mysql-test/suite/innodb/r/import_cfg.result new file mode 100644 index 00000000000..163252c4dac --- /dev/null +++ b/mysql-test/suite/innodb/r/import_cfg.result @@ -0,0 +1,67 @@ +# +# MDEV-35169 ALTER TABLE...IMPORT TABLESPACE does not +# work with INDEX DESC +# +# prepare cfg for primary key with desc column +create table t1 (pk int, a int, primary key(pk desc)) engine=InnoDB; +insert into t1 values (1,10),(2,20),(3,15); +flush table t1 for export; +unlock tables; +drop table t1; +# prepare cfg for secondary index with desc column +create table t1 (pk int primary key, a int,key(a desc)) engine=InnoDB; +insert into t1 values (1,10),(2,20),(3,15); +flush table t1 for export; +unlock tables; +drop table t1; +# prepare cfg for secondary index with ascending column +create table t1 (pk int primary key, a int, key(a)) engine=InnoDB; +insert into t1 values (1,10),(2,20),(3,15); +flush table t1 for export; +unlock tables; +drop table t1; +# Import desc tablespace into desc frm +# Import into table with desc primary key column +create table t1 (pk int, a int, primary key(pk desc)) engine=InnoDB; +alter table t1 discard tablespace; +alter table t1 import tablespace; +check table t1 extended; +Table Op Msg_type Msg_text +test.t1 check status OK +drop table t1; +# Import into table with desc secondary index +create table t1 (pk int primary key, a int, key(a desc))engine=InnoDB; +alter table t1 discard tablespace; +alter table t1 import tablespace; +check table t1 extended; +Table Op Msg_type Msg_text +test.t1 check status OK +drop table t1; +# Import asc tablespace into desc frm +create table t1 (pk int primary key, a int, key(a desc))engine=InnoDB; +alter table t1 discard tablespace; +alter table t1 import tablespace; +ERROR HY000: Schema mismatch (Index a field a is DESC which does not match with .cfg file) +check table t1 extended; +Table Op Msg_type Msg_text +test.t1 check Error Tablespace has been discarded for table `t1` +test.t1 check error Corrupt +drop table t1; +# Import desc tablespace into asc frm +create table t1 (pk int primary key, a int, key(a)) engine=InnoDB; +alter table t1 discard tablespace; +alter table t1 import tablespace; +ERROR HY000: Schema mismatch (Index a field a is ASC which does not match with .cfg file) +check table t1 extended; +Table Op Msg_type Msg_text +test.t1 check Error Tablespace has been discarded for table `t1` +test.t1 check error Corrupt +drop table t1; +# Import asc tablespace into asc frm +create table t1 (pk int primary key, a int, key(a)) engine=InnoDB; +alter table t1 discard tablespace; +alter table t1 import tablespace; +check table t1 extended; +Table Op Msg_type Msg_text +test.t1 check status OK +drop table t1; diff --git a/mysql-test/suite/innodb/r/innodb-wl5522-debug.result b/mysql-test/suite/innodb/r/innodb-wl5522-debug.result index 18819087e4a..ab62c03b110 100644 --- a/mysql-test/suite/innodb/r/innodb-wl5522-debug.result +++ b/mysql-test/suite/innodb/r/innodb-wl5522-debug.result @@ -158,16 +158,6 @@ SET SESSION debug_dbug=@saved_debug_dbug; DROP TABLE t1; CREATE TABLE t1 (c1 INT) ENGINE = Innodb; INSERT INTO t1 VALUES (1); -SET SESSION debug_dbug="+d,ib_export_io_write_failure_9"; -FLUSH TABLES t1 FOR EXPORT; -Warnings: -Warning 1811 IO Write error: (9, Bad file descriptor) t1.cfg flush() failed -Warning 1811 IO Write error: (9, Bad file descriptor) t1.cfg flose() failed -UNLOCK TABLES; -SET SESSION debug_dbug=@saved_debug_dbug; -DROP TABLE t1; -CREATE TABLE t1 (c1 INT) ENGINE = Innodb; -INSERT INTO t1 VALUES (1); SET SESSION debug_dbug="+d,ib_export_io_write_failure_10"; FLUSH TABLES t1 FOR EXPORT; Warnings: diff --git a/mysql-test/suite/innodb/t/import_cfg.test b/mysql-test/suite/innodb/t/import_cfg.test new file mode 100644 index 00000000000..297de767d5d --- /dev/null +++ b/mysql-test/suite/innodb/t/import_cfg.test @@ -0,0 +1,83 @@ +--source include/have_innodb.inc +--let $datadir= `select @@datadir` + +--echo # +--echo # MDEV-35169 ALTER TABLE...IMPORT TABLESPACE does not +--echo # work with INDEX DESC +--echo # + +--echo # prepare cfg for primary key with desc column +create table t1 (pk int, a int, primary key(pk desc)) engine=InnoDB; +insert into t1 values (1,10),(2,20),(3,15); +flush table t1 for export; +--copy_file $datadir/test/t1.ibd $datadir/test/t1_pk.ibd.desc +--copy_file $datadir/test/t1.cfg $datadir/test/t1_pk.cfg.desc +unlock tables; +drop table t1; + +--echo # prepare cfg for secondary index with desc column +create table t1 (pk int primary key, a int,key(a desc)) engine=InnoDB; +insert into t1 values (1,10),(2,20),(3,15); +flush table t1 for export; +--copy_file $datadir/test/t1.ibd $datadir/test/t1.ibd.desc +--copy_file $datadir/test/t1.cfg $datadir/test/t1.cfg.desc +unlock tables; +drop table t1; + +--echo # prepare cfg for secondary index with ascending column +create table t1 (pk int primary key, a int, key(a)) engine=InnoDB; +insert into t1 values (1,10),(2,20),(3,15); +flush table t1 for export; +--copy_file $datadir/test/t1.ibd $datadir/test/t1.ibd.asc +--copy_file $datadir/test/t1.cfg $datadir/test/t1.cfg.asc +unlock tables; +drop table t1; + +--echo # Import desc tablespace into desc frm + +--echo # Import into table with desc primary key column +create table t1 (pk int, a int, primary key(pk desc)) engine=InnoDB; +alter table t1 discard tablespace; +--copy_file $datadir/test/t1_pk.ibd.desc $datadir/test/t1.ibd +--copy_file $datadir/test/t1_pk.cfg.desc $datadir/test/t1.cfg +alter table t1 import tablespace; +check table t1 extended; +drop table t1; + +--echo # Import into table with desc secondary index +create table t1 (pk int primary key, a int, key(a desc))engine=InnoDB; +alter table t1 discard tablespace; +--copy_file $datadir/test/t1.ibd.desc $datadir/test/t1.ibd +--copy_file $datadir/test/t1.cfg.desc $datadir/test/t1.cfg +alter table t1 import tablespace; +check table t1 extended; +drop table t1; + +--echo # Import asc tablespace into desc frm +create table t1 (pk int primary key, a int, key(a desc))engine=InnoDB; +alter table t1 discard tablespace; +--copy_file $datadir/test/t1.ibd.asc $datadir/test/t1.ibd +--copy_file $datadir/test/t1.cfg.asc $datadir/test/t1.cfg +--error ER_TABLE_SCHEMA_MISMATCH +alter table t1 import tablespace; +check table t1 extended; +drop table t1; + +--echo # Import desc tablespace into asc frm +create table t1 (pk int primary key, a int, key(a)) engine=InnoDB; +alter table t1 discard tablespace; +--copy_file $datadir/test/t1.ibd.desc $datadir/test/t1.ibd +--copy_file $datadir/test/t1.cfg.desc $datadir/test/t1.cfg +--error ER_TABLE_SCHEMA_MISMATCH +alter table t1 import tablespace; +check table t1 extended; +drop table t1; + +--echo # Import asc tablespace into asc frm +create table t1 (pk int primary key, a int, key(a)) engine=InnoDB; +alter table t1 discard tablespace; +--copy_file $datadir/test/t1.ibd.asc $datadir/test/t1.ibd +--copy_file $datadir/test/t1.cfg.asc $datadir/test/t1.cfg +alter table t1 import tablespace; +check table t1 extended; +drop table t1; diff --git a/mysql-test/suite/innodb/t/innodb-wl5522-debug.test b/mysql-test/suite/innodb/t/innodb-wl5522-debug.test index 68330692390..44a7b4acf5f 100644 --- a/mysql-test/suite/innodb/t/innodb-wl5522-debug.test +++ b/mysql-test/suite/innodb/t/innodb-wl5522-debug.test @@ -280,22 +280,6 @@ SET SESSION debug_dbug=@saved_debug_dbug; DROP TABLE t1; -CREATE TABLE t1 (c1 INT) ENGINE = Innodb; -INSERT INTO t1 VALUES (1); - -SET SESSION debug_dbug="+d,ib_export_io_write_failure_9"; - ---replace_regex /, .*\).*t1.cfg/, Bad file descriptor) t1.cfg/ - -FLUSH TABLES t1 FOR EXPORT; - -UNLOCK TABLES; - -SET SESSION debug_dbug=@saved_debug_dbug; - -DROP TABLE t1; - - CREATE TABLE t1 (c1 INT) ENGINE = Innodb; INSERT INTO t1 VALUES (1); diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index 7035c33ccf4..4338ad034b1 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -1213,6 +1213,16 @@ row_import::match_index_columns( err = DB_ERROR; } + + if (cfg_field->descending != field->descending) { + ib_errf(thd, IB_LOG_LEVEL_ERROR, + ER_TABLE_SCHEMA_MISMATCH, + "Index %s field %s is %s which does " + "not match with .cfg file", + index->name(), field->name(), + field->descending ? "DESC" : "ASC"); + err = DB_ERROR; + } } return(err); @@ -2557,7 +2567,11 @@ row_import_cfg_read_index_fields( field->prefix_len = mach_read_from_4(ptr) & ((1U << 12) - 1); ptr += sizeof(ib_uint32_t); - field->fixed_len = mach_read_from_4(ptr) & ((1U << 10) - 1); + uint32_t fixed_len = mach_read_from_4(ptr); + + field->descending = bool(fixed_len >> 31); + + field->fixed_len = fixed_len & ((1U << 10) - 1); ptr += sizeof(ib_uint32_t); /* Include the NUL byte in the length. */ diff --git a/storage/innobase/row/row0quiesce.cc b/storage/innobase/row/row0quiesce.cc index 057b20c77bd..9fe30709824 100644 --- a/storage/innobase/row/row0quiesce.cc +++ b/storage/innobase/row/row0quiesce.cc @@ -47,45 +47,38 @@ row_quiesce_write_index_fields( FILE* file, /*!< in: file to write to */ THD* thd) /*!< in/out: session */ { - byte row[sizeof(ib_uint32_t) * 2]; + byte row[sizeof(ib_uint32_t) * 3]; for (ulint i = 0; i < index->n_fields; ++i) { byte* ptr = row; const dict_field_t* field = &index->fields[i]; mach_write_to_4(ptr, field->prefix_len); - ptr += sizeof(ib_uint32_t); + ptr += 4; - mach_write_to_4(ptr, field->fixed_len); - - DBUG_EXECUTE_IF("ib_export_io_write_failure_9", - close(fileno(file));); - - if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) { - - ib_senderrf( - thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR, - (ulong) errno, strerror(errno), - "while writing index fields."); - - return(DB_IO_ERROR); - } + /* Since maximum fixed length can be + DICT_ANTELOPE_MAX_INDEX_COL_LEN, InnoDB + can use the 0th bit to store the + field descending information */ + mach_write_to_4(ptr, field->fixed_len + | uint32_t(field->descending) << 31); + ptr += 4; const char* field_name = field->name ? field->name : ""; /* Include the NUL byte in the length. */ - ib_uint32_t len = static_cast(strlen(field_name) + 1); - mach_write_to_4(row, len); + uint32_t len = uint32_t(strlen(field_name) + 1); + mach_write_to_4(ptr, len); DBUG_EXECUTE_IF("ib_export_io_write_failure_10", close(fileno(file));); - if (fwrite(row, 1, sizeof(len), file) != sizeof(len) + if (fwrite(row, 1, sizeof(row), file) != sizeof(row) || fwrite(field_name, 1, len, file) != len) { ib_senderrf( thd, IB_LOG_LEVEL_WARN, ER_IO_WRITE_ERROR, (ulong) errno, strerror(errno), - "while writing index column."); + "while writing index fields."); return(DB_IO_ERROR); }