mirror of
https://github.com/MariaDB/server.git
synced 2025-05-28 13:01:41 +03:00
In commit 8f8ba758559e473f643baa0a0601d321c42517b9 (MDEV-27234) the data dictionary recovery was changed to use READ COMMITTED so that table-rebuild operations (OPTIMIZE TABLE, TRUNCATE TABLE, some forms of ALTER TABLE) would be recovered correctly. However, for operations that avoid a table rebuild thanks to being able to instantly ADD, DROP or reorder columns, recovery must use the READ UNCOMMITTED isolation level so that changes to the hidden metadata record can be rolled back. We will detect instant operations by detecting uncommitted changes to SYS_COLUMNS in case there is no uncommitted change of SYS_TABLES.ID for the table. In any table-rebuilding DDL operation, the SYS_TABLES.ID (and likely also the table name) will be updated. As part of rolling back the instant ALTER TABLE operation, after the operation on the hidden metadata record has been rolled back, a rollback of an INSERT into SYS_COLUMNS in row_undo_ins_remove_clust_rec() will invoke trx_t::evict_table() to discard the READ UNCOMMITTED definition of the table. After that, subsequent recovery steps will load and use the correct table definition. Reviewed by: Thirunarayanan Balathandayuthapani Tested by: Matthias Leich
228 lines
6.3 KiB
Plaintext
228 lines
6.3 KiB
Plaintext
--source include/have_innodb.inc
|
|
# The embedded server tests do not support restarting.
|
|
--source include/not_embedded.inc
|
|
--source include/have_debug.inc
|
|
--source include/have_debug_sync.inc
|
|
|
|
FLUSH TABLES;
|
|
|
|
let INNODB_PAGE_SIZE=`select @@innodb_page_size`;
|
|
let MYSQLD_DATADIR=`select @@datadir`;
|
|
|
|
--echo #
|
|
--echo # MDEV-11369: Instant ADD COLUMN for InnoDB
|
|
--echo #
|
|
|
|
CREATE TABLE t1(id INT PRIMARY KEY, c2 INT UNIQUE)
|
|
ENGINE=InnoDB ROW_FORMAT=REDUNDANT;
|
|
CREATE TABLE t2 LIKE t1;
|
|
INSERT INTO t1 VALUES(0,2);
|
|
INSERT INTO t2 VALUES(2,1);
|
|
ALTER TABLE t2 ADD COLUMN (c3 TEXT NOT NULL DEFAULT 'De finibus bonorum');
|
|
BEGIN;
|
|
INSERT INTO t2 VALUES(3,4,'accusantium doloremque laudantium');
|
|
|
|
connect ddl, localhost, root;
|
|
SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
|
|
--send
|
|
ALTER TABLE t1 ADD COLUMN (c3 TEXT NOT NULL DEFAULT ' et malorum');
|
|
|
|
connection default;
|
|
SET DEBUG_SYNC='now WAIT_FOR ddl';
|
|
SET GLOBAL innodb_flush_log_at_trx_commit=1;
|
|
COMMIT;
|
|
|
|
--source include/kill_mysqld.inc
|
|
disconnect ddl;
|
|
--source include/start_mysqld.inc
|
|
|
|
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
|
|
SELECT * FROM t1;
|
|
SELECT * FROM t2;
|
|
BEGIN;
|
|
DELETE FROM t1;
|
|
ROLLBACK;
|
|
--source include/wait_all_purged.inc
|
|
|
|
INSERT INTO t2 VALUES
|
|
(16,1551,'Omnium enim rerum'),(128,1571,' principia parva sunt');
|
|
|
|
connect ddl, localhost, root;
|
|
SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
|
|
--send
|
|
ALTER TABLE t2 DROP COLUMN c3, ADD COLUMN c5 TEXT DEFAULT 'naturam abhorrere';
|
|
|
|
connection default;
|
|
SET DEBUG_SYNC='now WAIT_FOR ddl';
|
|
SET GLOBAL innodb_flush_log_at_trx_commit=1;
|
|
UPDATE t1 SET c2=c2+1;
|
|
|
|
--source include/kill_mysqld.inc
|
|
disconnect ddl;
|
|
--source include/start_mysqld.inc
|
|
|
|
SET @saved_frequency= @@GLOBAL.innodb_purge_rseg_truncate_frequency;
|
|
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
|
|
|
|
SELECT * FROM t1;
|
|
SELECT * FROM t2;
|
|
BEGIN;
|
|
INSERT INTO t1 SET id=1;
|
|
DELETE FROM t2;
|
|
ROLLBACK;
|
|
--source include/wait_all_purged.inc
|
|
|
|
INSERT INTO t2 VALUES (64,42,'De finibus bonorum'), (347,33101,' et malorum');
|
|
|
|
connect ddl, localhost, root;
|
|
ALTER TABLE t2 DROP COLUMN c3;
|
|
SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
|
|
--send
|
|
ALTER TABLE t2 ADD COLUMN (c4 TEXT NOT NULL DEFAULT ' et malorum');
|
|
|
|
connection default;
|
|
SET DEBUG_SYNC='now WAIT_FOR ddl';
|
|
SET GLOBAL innodb_flush_log_at_trx_commit=1;
|
|
DELETE FROM t1;
|
|
|
|
--source include/kill_mysqld.inc
|
|
disconnect ddl;
|
|
--source include/start_mysqld.inc
|
|
|
|
SET GLOBAL innodb_purge_rseg_truncate_frequency=1;
|
|
|
|
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
|
|
let SEARCH_PATTERN= \[Note\] InnoDB: Rolled back recovered transaction ;
|
|
-- source include/search_pattern_in_file.inc
|
|
|
|
SELECT * FROM t1;
|
|
SELECT * FROM t2;
|
|
BEGIN;
|
|
INSERT INTO t1 SET id=1;
|
|
DELETE FROM t2;
|
|
ROLLBACK;
|
|
--source include/wait_all_purged.inc
|
|
|
|
FLUSH TABLE t1,t2 FOR EXPORT;
|
|
|
|
# At this point, t1 is empty and t2 contains a 'default row'.
|
|
|
|
# The following is based on innodb.table_flags and innodb.dml_purge:
|
|
--perl
|
|
use strict;
|
|
my $ps= $ENV{INNODB_PAGE_SIZE};
|
|
foreach my $table ('t1','t2') {
|
|
my $file= "$ENV{MYSQLD_DATADIR}/test/$table.ibd";
|
|
open(FILE, "<", $file) || die "Unable to open $file\n";
|
|
my $page;
|
|
sysseek(FILE, 3*$ps, 0) || die "Unable to seek $file";
|
|
die "Unable to read $file" unless sysread(FILE, $page, $ps) == $ps;
|
|
print "$table clustered index root page";
|
|
print "(type ", unpack("n", substr($page,24,2)), "):\n";
|
|
print "N_RECS=", unpack("n", substr($page,38+16,2));
|
|
print "; LEVEL=", unpack("n", substr($page,38+26,2)), "\n";
|
|
my @fields=("id","DB_TRX_ID","DB_ROLL_PTR", "c2","c3","c4");
|
|
for (my $offset= 0x65; $offset;
|
|
$offset= unpack("n", substr($page,$offset-2,2)))
|
|
{
|
|
print "header=0x", unpack("H*",substr($page,$offset-6,6)), " (";
|
|
my $n_fields= unpack("n", substr($page,$offset-4,2)) >> 1 & 0x3ff;
|
|
my $start= 0;
|
|
my $name;
|
|
if (unpack("C", substr($page,$offset-3,1)) & 1) {
|
|
for (my $i= 0; $i < $n_fields; $i++) {
|
|
my $end= unpack("C", substr($page, $offset-7-$i, 1));
|
|
print ",\n " if $i;
|
|
print "$fields[$i]=";
|
|
if ($end & 0x80) {
|
|
print "NULL(", ($end & 0x7f) - $start, " bytes)"
|
|
} else {
|
|
print "0x", unpack("H*", substr($page,$offset+$start,$end-$start))
|
|
}
|
|
$start= $end & 0x7f;
|
|
}
|
|
} else {
|
|
for (my $i= 0; $i < $n_fields; $i++) {
|
|
my $end= unpack("n", substr($page, $offset-8-2*$i, 2));
|
|
print ",\n " if $i;
|
|
if ($i > 2 && !(~unpack("C",substr($page,$offset-6,1)) & 0x30)) {
|
|
if ($i == 3) {
|
|
print "BLOB=";
|
|
$start += 8; # skip the space_id,page_number
|
|
} else {
|
|
print "$fields[$i - 1]=";
|
|
}
|
|
} else {
|
|
print "$fields[$i]=";
|
|
}
|
|
if ($end & 0x8000) {
|
|
print "NULL(", ($end & 0x7fff) - $start, " bytes)"
|
|
} else {
|
|
print "0x", unpack("H*", substr($page,$offset+$start,($end-$start) & 0x3fff))
|
|
}
|
|
$start= $end & 0x3fff;
|
|
}
|
|
}
|
|
print ")\n";
|
|
}
|
|
close(FILE) || die "Unable to close $file\n";
|
|
}
|
|
EOF
|
|
|
|
UNLOCK TABLES;
|
|
|
|
DELETE FROM t2;
|
|
--source include/wait_all_purged.inc
|
|
|
|
--echo #
|
|
--echo # MDEV-24323 Crash on recovery after kill during instant ADD COLUMN
|
|
--echo #
|
|
connect ddl, localhost, root;
|
|
CREATE TABLE t3(id INT PRIMARY KEY, c2 INT, v2 INT AS(c2) VIRTUAL, UNIQUE(v2))
|
|
ENGINE=InnoDB;
|
|
INSERT INTO t3 SET id=1,c2=1;
|
|
|
|
SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
|
|
--send
|
|
ALTER TABLE t3 ADD COLUMN c3 TEXT NOT NULL DEFAULT 'sic transit gloria mundi';
|
|
|
|
connection default;
|
|
SET DEBUG_SYNC='now WAIT_FOR ddl';
|
|
SET GLOBAL innodb_flush_log_at_trx_commit=1;
|
|
INSERT INTO t1 VALUES(0,0);
|
|
|
|
--source include/kill_mysqld.inc
|
|
disconnect ddl;
|
|
--source include/start_mysqld.inc
|
|
|
|
SHOW CREATE TABLE t1;
|
|
SHOW CREATE TABLE t2;
|
|
SHOW CREATE TABLE t3;
|
|
DROP TABLE t2,t3;
|
|
|
|
--echo #
|
|
--echo # MDEV-29440 InnoDB instant ALTER TABLE recovery wrongly uses
|
|
--echo # READ COMMITTED isolation level instead of READ UNCOMMITTED
|
|
--echo #
|
|
CREATE TABLE t2(a INT UNSIGNED PRIMARY KEY) ENGINE=InnoDB;
|
|
INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6);
|
|
|
|
connect ddl, localhost, root;
|
|
SET DEBUG_SYNC='innodb_alter_inplace_before_commit SIGNAL ddl WAIT_FOR ever';
|
|
--send
|
|
ALTER TABLE t2 ADD COLUMN b TINYINT UNSIGNED NOT NULL DEFAULT 42 FIRST;
|
|
|
|
connection default;
|
|
SET DEBUG_SYNC='now WAIT_FOR ddl';
|
|
SET GLOBAL innodb_flush_log_at_trx_commit=1;
|
|
DELETE FROM t1;
|
|
|
|
--source include/kill_mysqld.inc
|
|
disconnect ddl;
|
|
--source include/start_mysqld.inc
|
|
|
|
CHECK TABLE t2;
|
|
DROP TABLE t1,t2;
|
|
|
|
--list_files $MYSQLD_DATADIR/test
|