Commit 2d36352c authored by Yuchen Pei's avatar Yuchen Pei

MDEV-26137 Improve import tablespace workflow.

Allow ALTER TABLE ... IMPORT TABLESPACE without creating the table
followed by discarding the tablespace.

That is, assuming we want to import table t1 to t2, instead of

CREATE TABLE t2 LIKE t1;
ALTER TABLE t2 DISCARD TABLESPACE;
FLUSH TABLES t1 FOR EXPORT;
--copy_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/t2.cfg
--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;

We can simply do

FLUSH TABLES t1 FOR EXPORT;
--copy_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/t2.cfg
--copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/t2.frm
--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;

We achieve this by creating a "stub" table in the second scenario
while opening the table, where t2 does not exist but needs to import
from t1. The "stub" table is similar to a table that is created but
then instructed to discard its tablespace.

We include tests with various row formats, encryption, with indexes
and auto-increment.
Signed-off-by: default avatarYuchen Pei <yuchen.pei@mariadb.com>
parent 2b61ff8f
......@@ -20,3 +20,22 @@ SELECT * FROM t2;
f1 f2
1 InnoDB
DROP TABLE t1, t2;
#
# MDEV-26137 ALTER TABLE IMPORT enhancement
#
# with encryption and page_compressed
CREATE TABLE t1 (a int, b varchar(50)) ENGINE=InnoDB ENCRYPTED=YES ENCRYPTION_KEY_ID=6 PAGE_COMPRESSED=1;
INSERT INTO t1 VALUES(42, "hello");
FLUSH TABLES t1 FOR EXPORT;
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) DEFAULT NULL,
`b` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci `ENCRYPTED`=YES `ENCRYPTION_KEY_ID`=6 `PAGE_COMPRESSED`=1
SELECT * FROM t2;
a b
42 hello
DROP TABLE t1, t2;
......@@ -21,3 +21,22 @@ ALTER TABLE t2 DROP KEY idx;
ALTER TABLE t2 IMPORT TABLESPACE;
SELECT * FROM t2;
DROP TABLE t1, t2;
--echo #
--echo # MDEV-26137 ALTER TABLE IMPORT enhancement
--echo #
--echo # with encryption and page_compressed
CREATE TABLE t1 (a int, b varchar(50)) ENGINE=InnoDB ENCRYPTED=YES ENCRYPTION_KEY_ID=6 PAGE_COMPRESSED=1;
INSERT INTO t1 VALUES(42, "hello");
FLUSH TABLES t1 FOR EXPORT;
--copy_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/t2.cfg
--copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/t2.frm
--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;
SHOW CREATE TABLE t2;
SELECT * FROM t2;
DROP TABLE t1, t2;
# Embedded server uses absolute path, causing result mismatch in warning messages when .cfg is not copied over. Given we have tested importing without copying cfg in other tests, we don't do it here.
--source include/innodb_row_format.inc
--source include/innodb_row_format_2.inc
--source include/innodb_checksum_algorithm.inc
--echo #
--echo # MDEV-26137 ALTER TABLE IMPORT enhancement
--echo #
let $MYSQLD_DATADIR = `SELECT @@datadir`;
--disable_query_log
let $ROW_FORMAT_OPTION=;
if($MTR_COMBINATION_R_REDUNDANT) {
let $ROW_FORMAT_OPTION= ROW_FORMAT=REDUNDANT;
}
if($MTR_COMBINATION_R_COMPACT) {
let $ROW_FORMAT_OPTION= ROW_FORMAT=COMPACT;
}
if($MTR_COMBINATION_R_DYNAMIC) {
let $ROW_FORMAT_OPTION= ROW_FORMAT=DYNAMIC;
}
if($MTR_COMBINATION_R_COMPRESSED) {
let $ROW_FORMAT_OPTION= ROW_FORMAT=COMPRESSED;
}
let $CREATE_OPTIONS_REGEX= /ENGINE=InnoDB.*$ROW_FORMAT_OPTION/CREATE_OPTIONS/;
--enable_query_log
--replace_regex $CREATE_OPTIONS_REGEX
eval
CREATE TABLE t1(a INT PRIMARY KEY DEFAULT 42) ENGINE=InnoDB $ROW_FORMAT_OPTION;
INSERT INTO t1() VALUES();
FLUSH TABLES t1 FOR EXPORT;
--copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/t2.frm
--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;
--replace_regex $CREATE_OPTIONS_REGEX
SHOW CREATE TABLE t2;
SELECT * FROM t2;
DROP TABLE t1, t2;
[r_default]
[r_redundant]
[r_compact]
[r_dynamic]
[r_compressed]
# The goal of including this file is to add various row_format options
# combinations (see include/innodb_row_format_2.combinations)
--source include/have_innodb.inc
#
# MDEV-26137 ALTER TABLE IMPORT enhancement
#
CREATE TABLE t1(a INT PRIMARY KEY DEFAULT 42) CREATE_OPTIONS;
INSERT INTO t1() VALUES();
FLUSH TABLES t1 FOR EXPORT;
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) NOT NULL DEFAULT 42,
PRIMARY KEY (`a`)
) CREATE_OPTIONS
SELECT * FROM t2;
a
42
DROP TABLE t1, t2;
#
# MDEV-26137 ALTER TABLE IMPORT enhancement
#
CREATE TABLE t1(a INT PRIMARY KEY DEFAULT 42) CREATE_OPTIONS;
INSERT INTO t1() VALUES();
FLUSH TABLES t1 FOR EXPORT;
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;
Warnings:
Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t2.cfg', will attempt to import without schema verification
Warning 1810 IO Read error: (2, No such file or directory) Error opening './test/t2.cfg', will attempt to import without schema verification
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) NOT NULL DEFAULT 42,
PRIMARY KEY (`a`)
) CREATE_OPTIONS
SELECT * FROM t2;
a
42
DROP TABLE t1, t2;
#
# MDEV-26137 ALTER TABLE IMPORT enhancement
#
call mtr.add_suppression('InnoDB: Tablespace for table `test`.`t1` is set as discarded.');
call mtr.add_suppression('InnoDB: Tablespace for table `test`.`t2` is set as discarded.');
call mtr.add_suppression('InnoDB: Tablespace for table `test`.`t3` is set as discarded.');
call mtr.add_suppression('InnoDB: ./test/t3.ibd: Page 0 at offset 0 looks corrupted.');
call mtr.add_suppression("mariadbd.*: Index for table 't3' is corrupt; try to repair it");
call mtr.add_suppression("InnoDB: Corrupted page \\[page id: space=.*, page number=0\\] of datafile './test/t3.ibd' could not be found in the doublewrite buffer.");
call mtr.add_suppression('InnoDB: Tablespace for table `test`.`t4` is set as discarded.');
call mtr.add_suppression('InnoDB: ./test/t4.ibd: Page 0 at offset 0 looks corrupted.');
call mtr.add_suppression("mariadbd.*: Index for table 't4' is corrupt; try to repair it");
call mtr.add_suppression("InnoDB: Corrupted page \\[page id: space=.*, page number=0\\] of datafile './test/t4.ibd' could not be found in the doublewrite buffer.");
# Recovery from crashes
## t1: Creation of stub succeeds; server crashes; second import attempt succeeds
## t2: Creation of stub succeeds; server crashes; drop table
## t3: Creation of stub succeeds; server crashes; ibd corrupted; second import attempt fails; drop table
## t4: Did not copy .cfg; creation of stub succeeds; server crashes; ibd corrupted; second import attempt fails; drop table
CREATE TABLE t (a int) ENGINE=InnoDB;
INSERT INTO t VALUES(42);
FLUSH TABLES t FOR EXPORT;
UNLOCK TABLES;
connect hang1,localhost,root;
SET DEBUG_SYNC='ib_after_create_stub_for_import SIGNAL hung WAIT_FOR ever';
ALTER TABLE t1 IMPORT TABLESPACE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR hung';
connect hang2,localhost,root;
SET DEBUG_SYNC='ib_after_create_stub_for_import SIGNAL hung WAIT_FOR ever';
ALTER TABLE t2 IMPORT TABLESPACE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR hung';
connect hang3,localhost,root;
SET DEBUG_SYNC='ib_after_create_stub_for_import SIGNAL hung WAIT_FOR ever';
ALTER TABLE t3 IMPORT TABLESPACE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR hung';
connect hang4,localhost,root;
SET DEBUG_SYNC='ib_after_create_stub_for_import SIGNAL hung WAIT_FOR ever';
ALTER TABLE t4 IMPORT TABLESPACE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR hung';
# corrupting the 0th page
# Restart mysqld after the crash and reconnect.
# restart
ALTER TABLE t1 IMPORT TABLESPACE;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
SELECT * FROM t1;
a
42
ALTER TABLE t3 IMPORT TABLESPACE;
ERROR HY000: Internal error: Error importing tablespace for table `test`.`t3` : Data structure corruption
ALTER TABLE t4 IMPORT TABLESPACE;
ERROR HY000: Schema mismatch (Expected FSP_SPACE_FLAGS=0x15, .ibd file contains 0x1010115.)
DROP TABLE t, t1, t2, t3, t4;
# Recovery from corruption only, no server restart
## t5: Recovery from corruption, with cfg
## t6: Recovery from corruption, without cfg
call mtr.add_suppression('InnoDB: ./test/t5.ibd: Page 0 at offset 0 looks corrupted.');
call mtr.add_suppression("mariadbd.*: Index for table 't5' is corrupt; try to repair it");
call mtr.add_suppression("InnoDB: Corrupted page \\[page id: space=.*, page number=0\\] of datafile './test/t5.ibd' could not be found in the doublewrite buffer.");
call mtr.add_suppression("InnoDB: Corrupted page \\[page id: space=.*, page number=0\\] of datafile './test/t6.ibd' could not be found in the doublewrite buffer.");
call mtr.add_suppression("mariadbd.*: Index for table 't6' is corrupt; try to repair it");
CREATE TABLE t (a int) ENGINE=InnoDB;
INSERT INTO t VALUES(42);
FLUSH TABLES t FOR EXPORT;
UNLOCK TABLES;
# corrupting the 0th page
ALTER TABLE t5 IMPORT TABLESPACE;
ERROR HY000: Internal error: Error importing tablespace for table `test`.`t5` : Data structure corruption
ALTER TABLE t6 IMPORT TABLESPACE;
ERROR HY000: Schema mismatch (Expected FSP_SPACE_FLAGS=0x15, .ibd file contains 0x1010115.)
DROP TABLE t, t5, t6;
#
# MDEV-26137 ALTER TABLE IMPORT enhancement
#
# drop t1 before importing t2
CREATE TABLE t1 (a int) ENGINE=InnoDB;
INSERT INTO t1 VALUES(42);
FLUSH TABLES t1 FOR EXPORT;
UNLOCK TABLES;
DROP TABLE t1;
ALTER TABLE t2 IMPORT TABLESPACE;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
SELECT * FROM t2;
a
42
DROP TABLE t2;
# created t2 but did not discard tablespace
CREATE TABLE t1 (a int) ENGINE=InnoDB;
INSERT INTO t1 VALUES(42);
CREATE TABLE t2 LIKE t1;
FLUSH TABLES t1 FOR EXPORT;
UNLOCK TABLES;
DROP TABLE t1;
call mtr.add_suppression("InnoDB: Unable to import tablespace");
ALTER TABLE t2 IMPORT TABLESPACE;
ERROR HY000: Tablespace for table 'test/t2' exists. Please DISCARD the tablespace before IMPORT
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
SELECT * FROM t2;
a
DROP TABLE t2;
# attempt to import when there's no tablespace
ALTER TABLE t2 IMPORT TABLESPACE;
ERROR 42S02: Table 'test.t2' doesn't exist
# with index
CREATE TABLE t1 (a int, b varchar(50)) ENGINE=InnoDB;
CREATE UNIQUE INDEX ai ON t1 (a);
INSERT INTO t1 VALUES(42, "hello");
FLUSH TABLES t1 FOR EXPORT;
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) DEFAULT NULL,
`b` varchar(50) DEFAULT NULL,
UNIQUE KEY `ai` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
SELECT * FROM t2;
a b
42 hello
SHOW INDEX FROM t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Ignored
t1 0 ai 1 a A 1 NULL NULL YES BTREE NO
SHOW INDEX FROM t2;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Ignored
t2 0 ai 1 a A 1 NULL NULL YES BTREE NO
DROP TABLE t1, t2;
# with virtual column index
CREATE TABLE t1 (a int, b int as (a * a)) ENGINE=InnoDB;
CREATE UNIQUE INDEX ai ON t1 (b);
INSERT INTO t1 VALUES(42, default);
FLUSH TABLES t1 FOR EXPORT;
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` int(11) DEFAULT NULL,
`b` int(11) GENERATED ALWAYS AS (`a` * `a`) VIRTUAL,
UNIQUE KEY `ai` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
SELECT * FROM t2;
a b
42 1764
SELECT b FROM t2 USE INDEX (ai);
b
1764
SHOW INDEX FROM t1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Ignored
t1 0 ai 1 b A 1 NULL NULL YES BTREE NO
SHOW INDEX FROM t2;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Ignored
t2 0 ai 1 b A 1 NULL NULL YES BTREE NO
CHECK TABLE t2 EXTENDED;
Table Op Msg_type Msg_text
test.t2 check status OK
DROP TABLE t1, t2;
# with auto_increment
CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, i2 INT, i1 INT)ENGINE=INNODB;
INSERT INTO t1 (i2) SELECT 4 FROM seq_1_to_1024;
FLUSH TABLE t1 FOR EXPORT;
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;
CHECK TABLE t2 EXTENDED;
Table Op Msg_type Msg_text
test.t2 check status OK
DROP TABLE t2, t1;
--source include/import_begin.inc
--copy_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/t2.cfg
--source include/import_end.inc
# embedded server uses absolute path, causing result mismatch in warning messages
--source include/not_embedded.inc
--source include/import_begin.inc
--source include/import_end.inc
--source include/have_innodb.inc
--source include/have_debug.inc
--source include/not_embedded.inc
--echo #
--echo # MDEV-26137 ALTER TABLE IMPORT enhancement
--echo #
let MYSQLD_DATADIR = `SELECT @@datadir`;
let INNODB_PAGE_SIZE=`select @@innodb_page_size`;
call mtr.add_suppression('InnoDB: Tablespace for table `test`.`t1` is set as discarded.');
call mtr.add_suppression('InnoDB: Tablespace for table `test`.`t2` is set as discarded.');
call mtr.add_suppression('InnoDB: Tablespace for table `test`.`t3` is set as discarded.');
call mtr.add_suppression('InnoDB: ./test/t3.ibd: Page 0 at offset 0 looks corrupted.');
call mtr.add_suppression("mariadbd.*: Index for table 't3' is corrupt; try to repair it");
# In Windows etc.
call mtr.add_suppression("InnoDB: Corrupted page \\[page id: space=.*, page number=0\\] of datafile './test/t3.ibd' could not be found in the doublewrite buffer.");
call mtr.add_suppression('InnoDB: Tablespace for table `test`.`t4` is set as discarded.');
call mtr.add_suppression('InnoDB: ./test/t4.ibd: Page 0 at offset 0 looks corrupted.');
call mtr.add_suppression("mariadbd.*: Index for table 't4' is corrupt; try to repair it");
# In Windows etc.
call mtr.add_suppression("InnoDB: Corrupted page \\[page id: space=.*, page number=0\\] of datafile './test/t4.ibd' could not be found in the doublewrite buffer.");
--echo # Recovery from crashes
--echo ## t1: Creation of stub succeeds; server crashes; second import attempt succeeds
--echo ## t2: Creation of stub succeeds; server crashes; drop table
--echo ## t3: Creation of stub succeeds; server crashes; ibd corrupted; second import attempt fails; drop table
--echo ## t4: Did not copy .cfg; creation of stub succeeds; server crashes; ibd corrupted; second import attempt fails; drop table
CREATE TABLE t (a int) ENGINE=InnoDB;
INSERT INTO t VALUES(42);
FLUSH TABLES t FOR EXPORT;
--copy_file $MYSQLD_DATADIR/test/t.cfg $MYSQLD_DATADIR/test/t1.cfg
--copy_file $MYSQLD_DATADIR/test/t.frm $MYSQLD_DATADIR/test/t1.frm
--copy_file $MYSQLD_DATADIR/test/t.ibd $MYSQLD_DATADIR/test/t1.ibd
--copy_file $MYSQLD_DATADIR/test/t.cfg $MYSQLD_DATADIR/test/t2.cfg
--copy_file $MYSQLD_DATADIR/test/t.frm $MYSQLD_DATADIR/test/t2.frm
--copy_file $MYSQLD_DATADIR/test/t.ibd $MYSQLD_DATADIR/test/t2.ibd
--copy_file $MYSQLD_DATADIR/test/t.cfg $MYSQLD_DATADIR/test/t3.cfg
--copy_file $MYSQLD_DATADIR/test/t.frm $MYSQLD_DATADIR/test/t3.frm
--copy_file $MYSQLD_DATADIR/test/t.ibd $MYSQLD_DATADIR/test/t3.ibd
--copy_file $MYSQLD_DATADIR/test/t.frm $MYSQLD_DATADIR/test/t4.frm
--copy_file $MYSQLD_DATADIR/test/t.ibd $MYSQLD_DATADIR/test/t4.ibd
UNLOCK TABLES;
connect (hang1,localhost,root);
SET DEBUG_SYNC='ib_after_create_stub_for_import SIGNAL hung WAIT_FOR ever';
send ALTER TABLE t1 IMPORT TABLESPACE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR hung';
connect (hang2,localhost,root);
SET DEBUG_SYNC='ib_after_create_stub_for_import SIGNAL hung WAIT_FOR ever';
send ALTER TABLE t2 IMPORT TABLESPACE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR hung';
connect (hang3,localhost,root);
SET DEBUG_SYNC='ib_after_create_stub_for_import SIGNAL hung WAIT_FOR ever';
send ALTER TABLE t3 IMPORT TABLESPACE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR hung';
connect (hang4,localhost,root);
SET DEBUG_SYNC='ib_after_create_stub_for_import SIGNAL hung WAIT_FOR ever';
send ALTER TABLE t4 IMPORT TABLESPACE;
connection default;
SET DEBUG_SYNC='now WAIT_FOR hung';
let $shutdown_timeout=0;
--source include/shutdown_mysqld.inc
--echo # corrupting the 0th page
perl;
my $ps = $ENV{INNODB_PAGE_SIZE};
@tables= ('t3', 't4');
foreach $table (@tables) {
my $file = "$ENV{MYSQLD_DATADIR}/test/$table.ibd";
open(FILE, "+<$file") || die "Unable to open $file";
binmode FILE;
sysseek(FILE, 0, 0) || die "Unable to seek $file\n";
die "Unable to read $file" unless sysread(FILE, $page, $ps) == $ps;
# Replace all NUL bytes with SOH bytes.
$page =~ tr/\x0/\x1/;
sysseek(FILE, 0, 0) || die "Unable to seek $file\n";
syswrite(FILE, $page, $ps)==$ps || die "Unable to write $file\n";
close FILE or die "close";
}
EOF
--echo # Restart mysqld after the crash and reconnect.
--source include/start_mysqld.inc
ALTER TABLE t1 IMPORT TABLESPACE;
SHOW CREATE TABLE t1;
SELECT * FROM t1;
--error ER_INTERNAL_ERROR
ALTER TABLE t3 IMPORT TABLESPACE;
--error ER_TABLE_SCHEMA_MISMATCH
ALTER TABLE t4 IMPORT TABLESPACE;
DROP TABLE t, t1, t2, t3, t4;
--echo # Recovery from corruption only, no server restart
--echo ## t5: Recovery from corruption, with cfg
--echo ## t6: Recovery from corruption, without cfg
call mtr.add_suppression('InnoDB: ./test/t5.ibd: Page 0 at offset 0 looks corrupted.');
call mtr.add_suppression("mariadbd.*: Index for table 't5' is corrupt; try to repair it");
# In Windows etc.
call mtr.add_suppression("InnoDB: Corrupted page \\[page id: space=.*, page number=0\\] of datafile './test/t5.ibd' could not be found in the doublewrite buffer.");
# In Windows etc.
call mtr.add_suppression("InnoDB: Corrupted page \\[page id: space=.*, page number=0\\] of datafile './test/t6.ibd' could not be found in the doublewrite buffer.");
call mtr.add_suppression("mariadbd.*: Index for table 't6' is corrupt; try to repair it");
CREATE TABLE t (a int) ENGINE=InnoDB;
INSERT INTO t VALUES(42);
FLUSH TABLES t FOR EXPORT;
--copy_file $MYSQLD_DATADIR/test/t.cfg $MYSQLD_DATADIR/test/t5.cfg
--copy_file $MYSQLD_DATADIR/test/t.frm $MYSQLD_DATADIR/test/t5.frm
--copy_file $MYSQLD_DATADIR/test/t.ibd $MYSQLD_DATADIR/test/t5.ibd
--copy_file $MYSQLD_DATADIR/test/t.frm $MYSQLD_DATADIR/test/t6.frm
--copy_file $MYSQLD_DATADIR/test/t.ibd $MYSQLD_DATADIR/test/t6.ibd
UNLOCK TABLES;
--echo # corrupting the 0th page
perl;
my $ps = $ENV{INNODB_PAGE_SIZE};
@tables= ('t5', 't6');
foreach $table (@tables) {
my $file = "$ENV{MYSQLD_DATADIR}/test/$table.ibd";
open(FILE, "+<$file") || die "Unable to open $file";
binmode FILE;
sysseek(FILE, 0, 0) || die "Unable to seek $file\n";
die "Unable to read $file" unless sysread(FILE, $page, $ps) == $ps;
# Replace all NUL bytes with SOH bytes.
$page =~ tr/\x0/\x1/;
sysseek(FILE, 0, 0) || die "Unable to seek $file\n";
syswrite(FILE, $page, $ps)==$ps || die "Unable to write $file\n";
close FILE or die "close";
}
EOF
--error ER_INTERNAL_ERROR
ALTER TABLE t5 IMPORT TABLESPACE;
--error ER_TABLE_SCHEMA_MISMATCH
ALTER TABLE t6 IMPORT TABLESPACE;
DROP TABLE t, t5, t6;
--source include/have_innodb.inc
--source include/have_sequence.inc
let $MYSQLD_DATADIR = `SELECT @@datadir`;
--echo #
--echo # MDEV-26137 ALTER TABLE IMPORT enhancement
--echo #
--echo # drop t1 before importing t2
CREATE TABLE t1 (a int) ENGINE=InnoDB;
INSERT INTO t1 VALUES(42);
FLUSH TABLES t1 FOR EXPORT;
--copy_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/t2.cfg
--copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/t2.frm
--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd
UNLOCK TABLES;
DROP TABLE t1;
ALTER TABLE t2 IMPORT TABLESPACE;
SHOW CREATE TABLE t2;
SELECT * FROM t2;
DROP TABLE t2;
--echo # created t2 but did not discard tablespace
CREATE TABLE t1 (a int) ENGINE=InnoDB;
INSERT INTO t1 VALUES(42);
CREATE TABLE t2 LIKE t1;
FLUSH TABLES t1 FOR EXPORT;
--copy_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/t2.cfg
UNLOCK TABLES;
DROP TABLE t1;
call mtr.add_suppression("InnoDB: Unable to import tablespace");
--error ER_TABLESPACE_EXISTS
ALTER TABLE t2 IMPORT TABLESPACE;
SHOW CREATE TABLE t2;
SELECT * FROM t2;
DROP TABLE t2;
--echo # attempt to import when there's no tablespace
--error ER_NO_SUCH_TABLE
ALTER TABLE t2 IMPORT TABLESPACE;
--echo # with index
CREATE TABLE t1 (a int, b varchar(50)) ENGINE=InnoDB;
CREATE UNIQUE INDEX ai ON t1 (a);
INSERT INTO t1 VALUES(42, "hello");
FLUSH TABLES t1 FOR EXPORT;
--copy_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/t2.cfg
--copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/t2.frm
--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;
SHOW CREATE TABLE t2;
SELECT * FROM t2;
SHOW INDEX FROM t1;
SHOW INDEX FROM t2;
DROP TABLE t1, t2;
--echo # with virtual column index
CREATE TABLE t1 (a int, b int as (a * a)) ENGINE=InnoDB;
CREATE UNIQUE INDEX ai ON t1 (b);
INSERT INTO t1 VALUES(42, default);
FLUSH TABLES t1 FOR EXPORT;
--copy_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/t2.cfg
--copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/t2.frm
--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;
SHOW CREATE TABLE t2;
SELECT * FROM t2;
SELECT b FROM t2 USE INDEX (ai);
SHOW INDEX FROM t1;
SHOW INDEX FROM t2;
CHECK TABLE t2 EXTENDED;
DROP TABLE t1, t2;
--echo # with auto_increment
CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, i2 INT, i1 INT)ENGINE=INNODB;
INSERT INTO t1 (i2) SELECT 4 FROM seq_1_to_1024;
FLUSH TABLE t1 FOR EXPORT;
--copy_file $MYSQLD_DATADIR/test/t1.frm $MYSQLD_DATADIR/test/t2.frm
--copy_file $MYSQLD_DATADIR/test/t1.ibd $MYSQLD_DATADIR/test/t2.ibd
--copy_file $MYSQLD_DATADIR/test/t1.cfg $MYSQLD_DATADIR/test/t2.cfg
UNLOCK TABLES;
ALTER TABLE t2 IMPORT TABLESPACE;
CHECK TABLE t2 EXTENDED;
DROP TABLE t2, t1;
......@@ -7212,6 +7212,7 @@ alter_commands:
Lex->m_sql_cmd= new (thd->mem_root)
Sql_cmd_discard_import_tablespace(
Sql_cmd_discard_import_tablespace::IMPORT_TABLESPACE);
Lex->create_info.add(DDL_options_st::OPT_IMPORT_TABLESPACE);
if (unlikely(Lex->m_sql_cmd == NULL))
MYSQL_YYABORT;
}
......
......@@ -537,7 +537,8 @@ struct DDL_options_st
OPT_OR_REPLACE_SLAVE_GENERATED= 32,// REPLACE was added on slave, it was
// not in the original query on master.
OPT_IF_EXISTS= 64,
OPT_CREATE_SELECT= 128 // CREATE ... SELECT
OPT_CREATE_SELECT= 128, // CREATE ... SELECT
OPT_IMPORT_TABLESPACE= 256 // ALTER ... IMPORT TABLESPACE
};
private:
......@@ -566,6 +567,7 @@ struct DDL_options_st
bool like() const { return m_options & OPT_LIKE; }
bool if_exists() const { return m_options & OPT_IF_EXISTS; }
bool is_create_select() const { return m_options & OPT_CREATE_SELECT; }
bool import_tablespace() const { return m_options & OPT_IMPORT_TABLESPACE; }
void add(const DDL_options_st::Options other)
{
......
......@@ -5225,7 +5225,8 @@ create_table_info_t::create_table_info_t(
m_create_info(create_info),
m_table_name(table_name), m_table(NULL),
m_remote_path(remote_path),
m_innodb_file_per_table(file_per_table)
m_innodb_file_per_table(file_per_table),
m_creating_stub(thd_ddl_options(thd)->import_tablespace())
{
}
......@@ -5847,8 +5848,22 @@ ha_innobase::open(const char* name, int, uint)
DEBUG_SYNC(thd, "ib_open_after_dict_open");
if (NULL == ib_table) {
if (UNIV_LIKELY(ib_table != nullptr)) {
} else if (thd_ddl_options(thd)->import_tablespace()) {
/* If the table does not exist and we are trying to
import, create a "stub" table similar to the effects
of CREATE TABLE followed by ALTER TABLE ... DISCARD
TABLESPACE. */
HA_CREATE_INFO create_info;
if (int err = prepare_create_stub_for_import(thd, norm_name,
create_info))
DBUG_RETURN(err);
create(norm_name, table, &create_info, true, nullptr);
DEBUG_SYNC(thd, "ib_after_create_stub_for_import");
ib_table = open_dict_table(name, norm_name, is_part,
DICT_ERR_IGNORE_FK_NOKEY);
} else {
if (is_part) {
sql_print_error("Failed to open table %s.\n",
norm_name);
......@@ -10571,6 +10586,10 @@ create_table_info_t::create_table_def()
? doc_id_col : n_cols - num_v;
}
/* Assume the tablespace is not available until we are able to
import it.*/
table->file_unreadable = m_creating_stub;
if (DICT_TF_HAS_DATA_DIR(m_flags)) {
ut_a(strlen(m_remote_path));
......@@ -11584,6 +11603,10 @@ bool create_table_info_t::innobase_table_flags()
}
}
/* If we are trying to import a tablespace, mark tablespace as
discarded. */
m_flags2 |= ulint{m_creating_stub} << DICT_TF2_POS_DISCARDED;
row_type = m_create_info->row_type;
if (zip_ssize && zip_allowed) {
......@@ -12732,6 +12755,7 @@ int create_table_info_t::create_table(bool create_fk)
dict_table_get_all_fts_indexes(m_table, fts->indexes);
}
create_fk&= !m_creating_stub;
dberr_t err = create_fk ? create_foreign_keys() : DB_SUCCESS;
if (err == DB_SUCCESS) {
......@@ -13136,6 +13160,9 @@ ha_innobase::create(const char *name, TABLE *form, HA_CREATE_INFO *create_info,
}
if (!error)
/* We can't possibly have foreign key information when creating a
stub table for importing .frm / .cfg / .ibd because it is not
stored in any of these files. */
error= info.create_table(own_trx);
if (own_trx || (info.flags2() & DICT_TF2_TEMPORARY))
......@@ -13158,7 +13185,11 @@ ha_innobase::create(const char *name, TABLE *form, HA_CREATE_INFO *create_info,
if (!error)
{
dict_stats_update(info.table(), DICT_STATS_EMPTY_TABLE);
/* Skip stats update when creating a stub table for importing,
as it is not needed and would report error due to the table
not being readable yet. */
if (!info.creating_stub())
dict_stats_update(info.table(), DICT_STATS_EMPTY_TABLE);
if (!info.table()->is_temporary())
log_write_up_to(trx->commit_lsn, true);
info.table()->release();
......
......@@ -701,6 +701,8 @@ class create_table_info_t
ulint flags2() const
{ return(m_flags2); }
bool creating_stub() const { return UNIV_UNLIKELY(m_creating_stub); }
/** Get trx. */
trx_t* trx() const
{ return(m_trx); }
......@@ -767,6 +769,9 @@ class create_table_info_t
/** Table flags2 */
ulint m_flags2;
/** Whether we are creating a stub table for importing. */
const bool m_creating_stub;
};
/**
......
......@@ -264,7 +264,8 @@ use its own tablespace instead of the system tablespace. */
#define DICT_TF2_USE_FILE_PER_TABLE 16U
/** Set when we discard/detach the tablespace */
#define DICT_TF2_DISCARDED 32U
constexpr ulint DICT_TF2_POS_DISCARDED= 5;
constexpr ulint DICT_TF2_DISCARDED= 1U << DICT_TF2_POS_DISCARDED;
/** This bit is set if all aux table names (both common tables and
index tables) of a FTS table are in HEX format. */
......@@ -2097,8 +2098,9 @@ struct dict_table_t {
process of altering partitions */
unsigned skip_alter_undo:1;
/*!< whether this is in a single-table tablespace and the .ibd
file is missing or page decryption failed and page is corrupted */
/** whether this is in a single-table tablespace and the .ibd file
is believed to be missing or page decryption failed and page is
corrupted */
unsigned file_unreadable:1;
/** TRUE if the table object has been added to the dictionary cache. */
......
......@@ -33,6 +33,7 @@ Created 2012-02-08 by Sunny Bains
struct trx_t;
struct dict_table_t;
struct row_prebuilt_t;
struct HA_CREATE_INFO;
/*****************************************************************//**
Imports a tablespace. The space id in the .ibd file must match the space id
......@@ -64,4 +65,13 @@ dberr_t
row_import_update_index_root(trx_t* trx, dict_table_t* table, bool reset)
MY_ATTRIBUTE((nonnull, warn_unused_result));
/** Prepare the create info to create a new stub table for import.
@param thd Connection
@param name Table name, format: "db/table_name".
@param create_info The create info for creating a stub.
@return ER_ error code
@retval 0 on success */
int prepare_create_stub_for_import(THD *thd, const char *name,
HA_CREATE_INFO& create_info);
#endif /* row0import_h */
This diff is collapsed.
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