mirror of
https://github.com/MariaDB/server.git
synced 2025-01-31 02:51:44 +01:00
1bd681c8b3
This is a complete rewrite of DROP TABLE, also as part of other DDL, such as ALTER TABLE, CREATE TABLE...SELECT, TRUNCATE TABLE. The background DROP TABLE queue hack is removed. If a transaction needs to drop and create a table by the same name (like TRUNCATE TABLE does), it must first rename the table to an internal #sql-ib name. No committed version of the data dictionary will include any #sql-ib tables, because whenever a transaction renames a table to a #sql-ib name, it will also drop that table. Either the rename will be rolled back, or the drop will be committed. Data files will be unlinked after the transaction has been committed and a FILE_RENAME record has been durably written. The file will actually be deleted when the detached file handle returned by fil_delete_tablespace() will be closed, after the latches have been released. It is possible that a purge of the delete of the SYS_INDEXES record for the clustered index will execute fil_delete_tablespace() concurrently with the DDL transaction. In that case, the thread that arrives later will wait for the other thread to finish. HTON_TRUNCATE_REQUIRES_EXCLUSIVE_USE: A new handler flag. ha_innobase::truncate() now requires that all other references to the table be released in advance. This was implemented by Monty. ha_innobase::delete_table(): If CREATE TABLE..SELECT is detected, we will "hijack" the current transaction, drop the table in the current transaction and commit the current transaction. This essentially fixes MDEV-21602. There is a FIXME comment about making the check less failure-prone. ha_innobase::truncate(), ha_innobase::delete_table(): Implement a fast path for temporary tables. We will no longer allow temporary tables to use the adaptive hash index. dict_table_t::mdl_name: The original table name for the purpose of acquiring MDL in purge, to prevent a race condition between a DDL transaction that is dropping a table, and purge processing undo log records of DML that had executed before the DDL operation. For #sql-backup- tables during ALTER TABLE...ALGORITHM=COPY, the dict_table_t::mdl_name will differ from dict_table_t::name. dict_table_t::parse_name(): Use mdl_name instead of name. dict_table_rename_in_cache(): Update mdl_name. For the internal FTS_ tables of FULLTEXT INDEX, purge would acquire MDL on the FTS_ table name, but not on the main table, and therefore it would be able to run concurrently with a DDL transaction that is dropping the table. Previously, the DROP TABLE queue hack prevented a race between purge and DDL. For now, we introduce purge_sys.stop_FTS() to prevent purge from opening any table, while a DDL transaction that may drop FTS_ tables is in progress. The function fts_lock_table(), which will be invoked before the dictionary is locked, will wait for purge to release any table handles. trx_t::drop_table_statistics(): Drop statistics for the table. This replaces dict_stats_drop_index(). We will drop or rename persistent statistics atomically as part of DDL transactions. On lock conflict for dropping statistics, we will fail instantly with DB_LOCK_WAIT_TIMEOUT, because we will be holding the exclusive data dictionary latch. trx_t::commit_cleanup(): Separated from trx_t::commit_in_memory(). Relax an assertion around fts_commit() and allow DB_LOCK_WAIT_TIMEOUT in addition to DB_DUPLICATE_KEY. The call to fts_commit() is entirely misplaced here and may obviously break the consistency of transactions that affect FULLTEXT INDEX. It needs to be fixed separately. dict_table_t::n_foreign_key_checks_running: Remove (MDEV-21175). The counter was a work-around for missing meta-data locking (MDL) on the SQL layer, and not really needed in MariaDB. ER_TABLE_IN_FK_CHECK: Replaced with ER_UNUSED_28. HA_ERR_TABLE_IN_FK_CHECK: Remove. row_ins_check_foreign_constraints(): Do not acquire dict_sys.latch either. The SQL-layer MDL will protect us. This was reviewed by Thirunarayanan Balathandayuthapani and tested by Matthias Leich.
248 lines
9.5 KiB
Text
248 lines
9.5 KiB
Text
--source include/innodb_page_size.inc
|
|
# Embedded server tests do not support restarting
|
|
--source include/not_embedded.inc
|
|
|
|
--disable_query_log
|
|
call mtr.add_suppression("InnoDB: Table `mysql`\\.`innodb_table_stats` not found");
|
|
call mtr.add_suppression("InnoDB: incorrect flags in SYS_TABLES");
|
|
call mtr.add_suppression("InnoDB: Table test/t[cp] in InnoDB data dictionary contains invalid flags\\. SYS_TABLES\\.TYPE=(129|289|3873|1232[31]) SYS_TABLES\\.N_COLS=2147483649\\r?$");
|
|
call mtr.add_suppression("InnoDB: Table test/tr in InnoDB data dictionary contains invalid flags\\. SYS_TABLES\\.TYPE=65 SYS_TABLES\\.MIX_LEN=4294967295\\r?$");
|
|
call mtr.add_suppression("InnoDB: Refusing to load '\\..test.td\\.ibd' \\(id=3, flags=0x([2e]1)\\); dictionary contains id=3, flags=0x100\\1\\r?$");
|
|
call mtr.add_suppression("InnoDB: Refusing to load '\\..test.td\\.ibd' \\(id=3, flags=0x(1[2ae]1)\\); dictionary contains id=3, flags=0x10\\1\\r?$");
|
|
call mtr.add_suppression("InnoDB: Ignoring tablespace for `test`.`td` because it could not be opened\\.");
|
|
# FIXME: Remove the following spam due to invalid flags for test.td
|
|
call mtr.add_suppression("InnoDB: Operating system error number .* in a file operation");
|
|
call mtr.add_suppression("InnoDB: The error means the system cannot find the path specified");
|
|
call mtr.add_suppression("InnoDB: adjusting FSP_SPACE_FLAGS of file ");
|
|
call mtr.add_suppression("InnoDB: Parent table of FTS auxiliary table .* not found.");
|
|
FLUSH TABLES;
|
|
--enable_query_log
|
|
|
|
let INNODB_PAGE_SIZE=`select @@innodb_page_size`;
|
|
let MYSQLD_DATADIR=`select @@datadir`;
|
|
|
|
let bugdir= $MYSQLTEST_VARDIR/tmp/table_flags;
|
|
--mkdir $bugdir
|
|
--let SEARCH_FILE = $MYSQLTEST_VARDIR/log/mysqld.1.err
|
|
|
|
--let $d=--innodb-data-home-dir=$bugdir --innodb-log-group-home-dir=$bugdir
|
|
--let $d=$d --innodb-data-file-path=ibdata1:1M:autoextend
|
|
--let $d=$d --innodb-undo-tablespaces=0
|
|
--let $d=$d --innodb-purge-rseg-truncate-frequency=1
|
|
--let $d=$d --skip-innodb-fast-shutdown
|
|
--let $restart_noprint=1
|
|
--let $restart_parameters=$d --innodb-stats-persistent=0
|
|
--source include/restart_mysqld.inc
|
|
|
|
SET GLOBAL innodb_file_per_table=1;
|
|
CREATE TABLE tr(a INT PRIMARY KEY)ENGINE=InnoDB ROW_FORMAT=REDUNDANT;
|
|
CREATE TABLE tc(a INT PRIMARY KEY)ENGINE=InnoDB ROW_FORMAT=COMPACT;
|
|
CREATE TABLE td(a INT PRIMARY KEY)ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
|
|
SET innodb_strict_mode=OFF;
|
|
CREATE TABLE tz(a INT PRIMARY KEY)ENGINE=InnoDB ROW_FORMAT=COMPRESSED
|
|
KEY_BLOCK_SIZE=1;
|
|
SET innodb_strict_mode=ON;
|
|
# PAGE_COMPRESSED is supported starting with MariaDB 10.1.0
|
|
CREATE TABLE tp(a INT PRIMARY KEY)ENGINE=InnoDB ROW_FORMAT=DYNAMIC
|
|
PAGE_COMPRESSED=1 PAGE_COMPRESSION_LEVEL=9;
|
|
|
|
--source include/shutdown_mysqld.inc
|
|
--perl
|
|
use strict;
|
|
do "$ENV{MTR_SUITE_DIR}/include/crc32.pl";
|
|
my $ps= $ENV{INNODB_PAGE_SIZE};
|
|
my $file= "$ENV{bugdir}/ibdata1";
|
|
open(FILE, "+<", $file) || die "Unable to open $file\n";
|
|
die "Unable to read $file" unless sysread(FILE, $_, 58) == 58;
|
|
my $full_crc32 = unpack("N",substr($_,54,4)) & 0x10; # FIL_SPACE_FLAGS
|
|
# Read DICT_HDR_TABLES, the root page number of CLUST_IND (SYS_TABLES.NAME).
|
|
sysseek(FILE, 7*$ps+38+32, 0) || die "Unable to seek $file";
|
|
die "Unable to read $file" unless sysread(FILE, $_, 4) == 4;
|
|
my $sys_tables_root = unpack("N", $_);
|
|
my $page;
|
|
print "SYS_TABLES clustered index root page ($sys_tables_root):\n";
|
|
sysseek(FILE, $sys_tables_root*$ps, 0) || die "Unable to seek $file";
|
|
die "Unable to read $file" unless sysread(FILE, $page, $ps) == $ps;
|
|
open(BACKUP, ">$ENV{bugdir}/sys_tables.bin") || die "Unable to open backup\n";
|
|
syswrite(BACKUP, $page, $ps)==$ps || die "Unable to write backup\n";
|
|
close(BACKUP) || die "Unable to close backup\n";
|
|
print "N_RECS=", unpack("n", substr($page,38+16,2));
|
|
print "; LEVEL=", unpack("n", substr($page,38+26,2));
|
|
print "; INDEX_ID=0x", unpack("H*", substr($page,38+28,8)), "\n";
|
|
my @fields=("NAME","DB_TRX_ID","DB_ROLL_PTR",
|
|
"ID","N_COLS","TYPE","MIX_ID","MIX_LEN","CLUSTER_NAME","SPACE");
|
|
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;
|
|
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)"
|
|
} elsif ($n_fields > 1 && $i == 0) {
|
|
$name= substr($page,$offset+$start,$end-$start);
|
|
print "'$name'"
|
|
} else {
|
|
print "0x", unpack("H*", substr($page,$offset+$start,$end-$start))
|
|
}
|
|
# Corrupt SYS_TABLES.TYPE
|
|
if ($i == 5)
|
|
{
|
|
my $flags= 0;
|
|
if ($name eq 'test/tr') {
|
|
$flags= 0x40 # DATA_DIR (largely ignored by 10.1+)
|
|
} elsif ($name eq 'test/tc') {
|
|
$flags= 0x80 # 10.1 PAGE_COMPRESSED
|
|
} elsif ($name eq 'test/td') {
|
|
$flags= 0xf00 # PAGE_COMPRESSION_LEVEL=15 (0..9 is valid)
|
|
# As part of the MDEV-12873 fix, because the
|
|
# PAGE_COMPRESSED=YES flag was not set, we will assume that
|
|
# this table was actually created with 10.2.2..10.2.6
|
|
# using PAGE_COMPRESSED=YES PAGE_COMPRESSION_LEVEL=7.
|
|
} elsif ($name eq 'test/tz') {
|
|
$flags= 0x3000 # 10.1 ATOMIC_WRITES=3 (0..2 is valid)
|
|
} elsif ($name eq 'test/tp') {
|
|
$flags= 0x880 # 10.1 PAGE_COMPRESSED, PAGE_COMPRESSION_LEVEL=8
|
|
# (in 10.2.2 through 10.2.6, this is interpreted as
|
|
# PAGE_COMPRESSION_LEVEL=4 without PAGE_COMPRESSED
|
|
# but with SHARED_SPACE, which should be invalid)
|
|
}
|
|
|
|
substr($page,$offset+$start,$end-$start)= pack(
|
|
"N", $flags ^
|
|
unpack("N", substr($page,$offset+$start,$end-$start)))
|
|
if $flags;
|
|
}
|
|
# Corrupt SYS_TABLES.MIX_LEN (ignored for ROW_FORMAT=REDUNDANT)
|
|
if ($i == 7 && $name eq 'test/tr')
|
|
{
|
|
substr($page,$offset+$start,$end-$start)= chr(255) x 4;
|
|
}
|
|
$start= $end & 0x7f;
|
|
}
|
|
print ")\n";
|
|
}
|
|
my $polynomial = 0x82f63b78; # CRC-32C
|
|
if ($full_crc32)
|
|
{
|
|
my $ck = mycrc32(substr($page, 0, $ps-4), 0, $polynomial);
|
|
substr($page, $ps-4, 4) = pack("N", $ck);
|
|
}
|
|
else
|
|
{
|
|
my $ck= pack("N",mycrc32(substr($page, 4, 22), 0, $polynomial) ^
|
|
mycrc32(substr($page, 38, $ps - 38 - 8), 0, $polynomial));
|
|
substr($page,0,4)=$ck;
|
|
substr($page,$ps-8,4)=$ck;
|
|
}
|
|
sysseek(FILE, $sys_tables_root*$ps, 0) || die "Unable to seek $file";
|
|
syswrite(FILE, $page, $ps)==$ps || die "Unable to write $file\n";
|
|
close(FILE) || die "Unable to close $file\n";
|
|
EOF
|
|
--source include/start_mysqld.inc
|
|
|
|
--error ER_NO_SUCH_TABLE_IN_ENGINE
|
|
SHOW CREATE TABLE tr;
|
|
--error ER_NO_SUCH_TABLE_IN_ENGINE
|
|
SHOW CREATE TABLE tc;
|
|
--error ER_NO_SUCH_TABLE_IN_ENGINE
|
|
SELECT * FROM tc;
|
|
SHOW CREATE TABLE td;
|
|
SELECT * FROM td;
|
|
# This table was converted to NO_ROLLBACK due to the SYS_TABLES.TYPE change.
|
|
SHOW CREATE TABLE tz;
|
|
BEGIN;
|
|
INSERT INTO tz VALUES(42);
|
|
ROLLBACK;
|
|
SELECT * FROM tz;
|
|
--error ER_NO_SUCH_TABLE_IN_ENGINE
|
|
SHOW CREATE TABLE tp;
|
|
|
|
--source include/shutdown_mysqld.inc
|
|
|
|
let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err;
|
|
--let SEARCH_PATTERN= InnoDB: Table test/t[cp] in InnoDB data dictionary contains invalid flags\. SYS_TABLES\.TYPE=(129|289|3873|1232[13]) SYS_TABLES\.N_COLS=2147483649
|
|
--source include/search_pattern_in_file.inc
|
|
--let SEARCH_PATTERN= InnoDB: Table test/tr in InnoDB data dictionary contains invalid flags\. SYS_TABLES\.TYPE=65 SYS_TABLES\.MIX_LEN=4294967295\b
|
|
--source include/search_pattern_in_file.inc
|
|
|
|
# Restore the backup of the corrupted SYS_TABLES clustered index root page
|
|
--perl
|
|
use strict;
|
|
my $ps= $ENV{INNODB_PAGE_SIZE};
|
|
my $file= "$ENV{bugdir}/ibdata1";
|
|
open(FILE, "+<", $file) || die "Unable to open $file\n";
|
|
open(BACKUP, "<$ENV{bugdir}/sys_tables.bin") || die "Unable to open backup\n";
|
|
# Read DICT_HDR_TABLES, the root page number of CLUST_IND (SYS_TABLES.NAME).
|
|
sysseek(FILE, 7*$ps+38+32, 0) || die "Unable to seek $file";
|
|
die "Unable to read $file\n" unless sysread(FILE, $_, 4) == 4;
|
|
my $sys_tables_root = unpack("N", $_);
|
|
print "Restoring SYS_TABLES clustered index root page ($sys_tables_root)\n";
|
|
sysseek(FILE, $sys_tables_root*$ps, 0) || die "Unable to seek $file";
|
|
die "Unable to read backup\n" unless sysread(BACKUP, $_, $ps) == $ps;
|
|
die "Unable to restore backup\n" unless syswrite(FILE, $_, $ps) == $ps;
|
|
close(BACKUP);
|
|
close(FILE) || die "Unable to close $file\n";
|
|
EOF
|
|
--source include/start_mysqld.inc
|
|
|
|
SHOW CREATE TABLE tr;
|
|
SHOW CREATE TABLE tc;
|
|
SHOW CREATE TABLE td;
|
|
SHOW CREATE TABLE tz;
|
|
SHOW CREATE TABLE tp;
|
|
|
|
BEGIN;
|
|
INSERT INTO tr VALUES(1);
|
|
INSERT INTO tc VALUES(1);
|
|
INSERT INTO td VALUES(1);
|
|
# We cannot access tz, because due to our fiddling of the NO_ROLLBACK flag,
|
|
# it now has a record with DB_TRX_ID=0, which is invalid for
|
|
# transactional tables until MDEV-12288 is implemented.
|
|
# INSERT INTO tz VALUES(1);
|
|
INSERT INTO tp VALUES(1);
|
|
ROLLBACK;
|
|
|
|
SELECT * FROM tr;
|
|
SELECT * FROM tc;
|
|
SELECT * FROM td;
|
|
# SELECT * FROM tz;
|
|
SELECT * FROM tp;
|
|
|
|
DROP TABLE tr,tc,td,tz,tp;
|
|
|
|
--let $restart_parameters=
|
|
--source include/restart_mysqld.inc
|
|
|
|
--error 0,1
|
|
--remove_file $bugdir/ibtmp1
|
|
--error 0,1
|
|
--remove_file $bugdir/ib_buffer_pool
|
|
|
|
--list_files $bugdir
|
|
--remove_files_wildcard $bugdir
|
|
--rmdir $bugdir
|
|
|
|
call mtr.add_suppression("ERROR HY000: Can't create table `test`.`t1`");
|
|
--error ER_CANT_CREATE_TABLE
|
|
CREATE TABLE t1(f1 INT, f2 VARCHAR(1), KEY k1(f2),
|
|
FULLTEXT KEY(f2),
|
|
FOREIGN KEY (f2) REFERENCES t1(f3))ENGINE=InnoDB;
|
|
|
|
--echo #
|
|
--echo # MDEV-23199 page_compression flag is missing
|
|
--echo # for full_crc32 tablespace
|
|
--echo #
|
|
|
|
CREATE TABLE t1(f1 BIGINT PRIMARY KEY)ENGINE=InnoDB;
|
|
INSERT INTO t1 VALUES(1);
|
|
ALTER TABLE t1 PAGE_COMPRESSED = 1;
|
|
INSERT INTO t1 VALUES(2);
|
|
let $shutdown_timeout = 0;
|
|
--source include/restart_mysqld.inc
|
|
DROP TABLE t1;
|