1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

MDEV-25506 (3 of 3): Do not delete .ibd files before commit

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.
This commit is contained in:
Marko Mäkelä
2021-06-09 17:02:55 +03:00
parent 3f78fbc582
commit 1bd681c8b3
98 changed files with 2180 additions and 3804 deletions

View File

@@ -1,4 +1,4 @@
/* Copyright (c) 2013 SkySQL Ab /* Copyright (c) 2013, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -74,7 +74,6 @@
{ "HA_ERR_INDEX_COL_TOO_LONG", HA_ERR_INDEX_COL_TOO_LONG, "" }, { "HA_ERR_INDEX_COL_TOO_LONG", HA_ERR_INDEX_COL_TOO_LONG, "" },
{ "HA_ERR_INDEX_CORRUPT", HA_ERR_INDEX_CORRUPT, "" }, { "HA_ERR_INDEX_CORRUPT", HA_ERR_INDEX_CORRUPT, "" },
{ "HA_ERR_UNDO_REC_TOO_BIG", HA_ERR_UNDO_REC_TOO_BIG, "" }, { "HA_ERR_UNDO_REC_TOO_BIG", HA_ERR_UNDO_REC_TOO_BIG, "" },
{ "HA_ERR_TABLE_IN_FK_CHECK", HA_ERR_TABLE_IN_FK_CHECK, "" },
{ "HA_ERR_ROW_NOT_VISIBLE", HA_ERR_ROW_NOT_VISIBLE, "" }, { "HA_ERR_ROW_NOT_VISIBLE", HA_ERR_ROW_NOT_VISIBLE, "" },
{ "HA_ERR_ABORTED_BY_USER", HA_ERR_ABORTED_BY_USER, "" }, { "HA_ERR_ABORTED_BY_USER", HA_ERR_ABORTED_BY_USER, "" },
{ "HA_ERR_DISK_FULL", HA_ERR_DISK_FULL, "" }, { "HA_ERR_DISK_FULL", HA_ERR_DISK_FULL, "" },

View File

@@ -513,7 +513,7 @@ enum ha_base_keytype {
#define HA_ERR_INDEX_CORRUPT 180 /* Index corrupted */ #define HA_ERR_INDEX_CORRUPT 180 /* Index corrupted */
#define HA_ERR_UNDO_REC_TOO_BIG 181 /* Undo log record too big */ #define HA_ERR_UNDO_REC_TOO_BIG 181 /* Undo log record too big */
#define HA_FTS_INVALID_DOCID 182 /* Invalid InnoDB Doc ID */ #define HA_FTS_INVALID_DOCID 182 /* Invalid InnoDB Doc ID */
#define HA_ERR_TABLE_IN_FK_CHECK 183 /* Table being used in foreign key check */ /* #define HA_ERR_TABLE_IN_FK_CHECK 183 */ /* Table being used in foreign key check */
#define HA_ERR_TABLESPACE_EXISTS 184 /* The tablespace existed in storage engine */ #define HA_ERR_TABLESPACE_EXISTS 184 /* The tablespace existed in storage engine */
#define HA_ERR_TOO_MANY_FIELDS 185 /* Table has too many columns */ #define HA_ERR_TOO_MANY_FIELDS 185 /* Table has too many columns */
#define HA_ERR_ROW_IN_WRONG_PARTITION 186 /* Row in wrong partition */ #define HA_ERR_ROW_IN_WRONG_PARTITION 186 /* Row in wrong partition */

View File

@@ -97,10 +97,7 @@ call mtr.check_testcase();
let $datadir=`select @@datadir`; let $datadir=`select @@datadir`;
list_files $datadir mysql_upgrade_info; list_files $datadir mysql_upgrade_info;
list_files_write_file $datadir.tempfiles.txt $datadir/test #sql*; list_files $datadir/test #sql*;
--replace_regex /#sql-ib[1-9][0-9]*\.ibd\n//
cat_file $datadir.tempfiles.txt;
remove_file $datadir.tempfiles.txt;
list_files $datadir/mysql #sql*; list_files $datadir/mysql #sql*;
# #

View File

@@ -86,11 +86,7 @@ show create table t1;
--error ER_CONSTRAINT_FAILED --error ER_CONSTRAINT_FAILED
insert t1 values (2, '2020-01-03', 20); insert t1 values (2, '2020-01-03', 20);
drop table t1; drop table t1;
--let $regexp=/#sql-ib[0-9a-f]+\.ibd\n// --list_files $datadir/test
--list_files_write_file $datadir.files.txt $datadir/test
--replace_regex $regexp
--cat_file $datadir.files.txt
--remove_file $datadir.files.txt
# MyISAM, different execution path # MyISAM, different execution path
create table t1(id int, d date not null, b bool not null default 0, primary key(id,d)) create table t1(id int, d date not null, b bool not null default 0, primary key(id,d))
@@ -106,10 +102,7 @@ show create table t1;
--error ER_CONSTRAINT_FAILED --error ER_CONSTRAINT_FAILED
insert t1 values (2, '2020-01-03', 20); insert t1 values (2, '2020-01-03', 20);
drop table t1; drop table t1;
--list_files_write_file $datadir.files.txt $datadir/test --list_files $datadir/test
--replace_regex $regexp
--cat_file $datadir.files.txt
--remove_file $datadir.files.txt
# #
# MDEV-13097 Online alter of a partitioned MyISAM table with auto_increment # MDEV-13097 Online alter of a partitioned MyISAM table with auto_increment

View File

@@ -38,10 +38,6 @@ idxa n_diff_pfx01 a
idxa n_diff_pfx02 a,DB_ROW_ID idxa n_diff_pfx02 a,DB_ROW_ID
idxa n_leaf_pages Number of leaf pages in the index idxa n_leaf_pages Number of leaf pages in the index
idxa size Number of pages in the index idxa size Number of pages in the index
idxb n_diff_pfx01 b
idxb n_diff_pfx02 b,DB_ROW_ID
idxb n_leaf_pages Number of leaf pages in the index
idxb size Number of pages in the index
vidxcd n_diff_pfx01 c vidxcd n_diff_pfx01 c
vidxcd n_diff_pfx02 c,d vidxcd n_diff_pfx02 c,d
vidxcd n_diff_pfx03 c,d,DB_ROW_ID vidxcd n_diff_pfx03 c,d,DB_ROW_ID
@@ -58,14 +54,6 @@ index_name stat_name stat_description
GEN_CLUST_INDEX n_diff_pfx01 DB_ROW_ID GEN_CLUST_INDEX n_diff_pfx01 DB_ROW_ID
GEN_CLUST_INDEX n_leaf_pages Number of leaf pages in the index GEN_CLUST_INDEX n_leaf_pages Number of leaf pages in the index
GEN_CLUST_INDEX size Number of pages in the index GEN_CLUST_INDEX size Number of pages in the index
idxb n_diff_pfx01 b
idxb n_diff_pfx02 b,DB_ROW_ID
idxb n_leaf_pages Number of leaf pages in the index
idxb size Number of pages in the index
vidxcd n_diff_pfx01 d
vidxcd n_diff_pfx02 d,DB_ROW_ID
vidxcd n_leaf_pages Number of leaf pages in the index
vidxcd size Number of pages in the index
ALTER TABLE t ADD INDEX vidxe (e), ALGORITHM=INPLACE; ALTER TABLE t ADD INDEX vidxe (e), ALGORITHM=INPLACE;
select count(*) from t; select count(*) from t;
count(*) count(*)
@@ -77,18 +65,6 @@ index_name stat_name stat_description
GEN_CLUST_INDEX n_diff_pfx01 DB_ROW_ID GEN_CLUST_INDEX n_diff_pfx01 DB_ROW_ID
GEN_CLUST_INDEX n_leaf_pages Number of leaf pages in the index GEN_CLUST_INDEX n_leaf_pages Number of leaf pages in the index
GEN_CLUST_INDEX size Number of pages in the index GEN_CLUST_INDEX size Number of pages in the index
idxb n_diff_pfx01 b
idxb n_diff_pfx02 b,DB_ROW_ID
idxb n_leaf_pages Number of leaf pages in the index
idxb size Number of pages in the index
vidxcd n_diff_pfx01 d
vidxcd n_diff_pfx02 d,DB_ROW_ID
vidxcd n_leaf_pages Number of leaf pages in the index
vidxcd size Number of pages in the index
vidxe n_diff_pfx01 e
vidxe n_diff_pfx02 e,DB_ROW_ID
vidxe n_leaf_pages Number of leaf pages in the index
vidxe size Number of pages in the index
ALTER TABLE t ADD COLUMN f INT GENERATED ALWAYS AS(a + a), ADD INDEX vidxf (f), ALGORITHM=INPLACE; ALTER TABLE t ADD COLUMN f INT GENERATED ALWAYS AS(a + a), ADD INDEX vidxf (f), ALGORITHM=INPLACE;
select count(*) from t; select count(*) from t;
count(*) count(*)
@@ -100,22 +76,6 @@ index_name stat_name stat_description
GEN_CLUST_INDEX n_diff_pfx01 DB_ROW_ID GEN_CLUST_INDEX n_diff_pfx01 DB_ROW_ID
GEN_CLUST_INDEX n_leaf_pages Number of leaf pages in the index GEN_CLUST_INDEX n_leaf_pages Number of leaf pages in the index
GEN_CLUST_INDEX size Number of pages in the index GEN_CLUST_INDEX size Number of pages in the index
idxb n_diff_pfx01 b
idxb n_diff_pfx02 b,DB_ROW_ID
idxb n_leaf_pages Number of leaf pages in the index
idxb size Number of pages in the index
vidxcd n_diff_pfx01 d
vidxcd n_diff_pfx02 d,DB_ROW_ID
vidxcd n_leaf_pages Number of leaf pages in the index
vidxcd size Number of pages in the index
vidxe n_diff_pfx01 e
vidxe n_diff_pfx02 e,DB_ROW_ID
vidxe n_leaf_pages Number of leaf pages in the index
vidxe size Number of pages in the index
vidxf n_diff_pfx01 f
vidxf n_diff_pfx02 f,DB_ROW_ID
vidxf n_leaf_pages Number of leaf pages in the index
vidxf size Number of pages in the index
ALTER TABLE t DROP INDEX vidxcd; ALTER TABLE t DROP INDEX vidxcd;
SELECT index_name, stat_name, stat_description SELECT index_name, stat_name, stat_description
FROM mysql.innodb_index_stats FROM mysql.innodb_index_stats
@@ -124,16 +84,4 @@ index_name stat_name stat_description
GEN_CLUST_INDEX n_diff_pfx01 DB_ROW_ID GEN_CLUST_INDEX n_diff_pfx01 DB_ROW_ID
GEN_CLUST_INDEX n_leaf_pages Number of leaf pages in the index GEN_CLUST_INDEX n_leaf_pages Number of leaf pages in the index
GEN_CLUST_INDEX size Number of pages in the index GEN_CLUST_INDEX size Number of pages in the index
idxb n_diff_pfx01 b
idxb n_diff_pfx02 b,DB_ROW_ID
idxb n_leaf_pages Number of leaf pages in the index
idxb size Number of pages in the index
vidxe n_diff_pfx01 e
vidxe n_diff_pfx02 e,DB_ROW_ID
vidxe n_leaf_pages Number of leaf pages in the index
vidxe size Number of pages in the index
vidxf n_diff_pfx01 f
vidxf n_diff_pfx02 f,DB_ROW_ID
vidxf n_leaf_pages Number of leaf pages in the index
vidxf size Number of pages in the index
DROP TABLE t; DROP TABLE t;

View File

@@ -15,6 +15,5 @@ SELECT t.name 'Table Name',
WHERE t.name not like 'SYS_%' WHERE t.name not like 'SYS_%'
AND t.name NOT LIKE 'mysql/%' AND t.name NOT LIKE 'mysql/%'
AND t.name NOT LIKE 'sys/%' AND t.name NOT LIKE 'sys/%'
AND t.name NOT LIKE '%/#sql-ib%'
ORDER BY t.name; ORDER BY t.name;
--enable_query_log --enable_query_log

View File

@@ -11,6 +11,6 @@ SELECT name 'Space_Name',
filename 'Path' filename 'Path'
FROM information_schema.innodb_sys_tablespaces FROM information_schema.innodb_sys_tablespaces
WHERE name != 'innodb_system' WHERE name != 'innodb_system'
AND name NOT LIKE 'mysql/%' AND name NOT LIKE '%/#sql-ib%' AND name NOT LIKE 'mysql/%'
ORDER BY space; ORDER BY space;
--enable_query_log --enable_query_log

View File

@@ -1,25 +0,0 @@
CREATE TABLE t(c0 SERIAL, c1 INT, c2 INT, c3 INT, c4 INT,
KEY(c1), KEY(c2), KEY(c2,c1),
KEY(c3), KEY(c3,c1), KEY(c3,c2), KEY(c3,c2,c1),
KEY(c4), KEY(c4,c1), KEY(c4,c2), KEY(c4,c2,c1),
KEY(c4,c3), KEY(c4,c3,c1), KEY(c4,c3,c2), KEY(c4,c3,c2,c1)) ENGINE=InnoDB;
CREATE TABLE `#mysql50##sql-ib-foo`(a SERIAL) ENGINE=InnoDB;
INSERT INTO t (c1) VALUES (1),(2),(1);
SET DEBUG_DBUG='+d,row_drop_table_add_to_background';
CREATE TABLE target (PRIMARY KEY(c1)) ENGINE=InnoDB SELECT * FROM t;
ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
SELECT * from target;
ERROR 42S02: Table 'test.target' doesn't exist
DROP TABLE t;
# restart
CREATE TABLE t (a INT) ENGINE=InnoDB;
DROP TABLE t;
DROP TABLE target;
ERROR 42S02: Unknown table 'test.target'
CREATE TABLE target (a INT) ENGINE=InnoDB;
DROP TABLE target;
SELECT * FROM `#mysql50##sql-ib-foo`;
ERROR 42S02: Table 'test.#mysql50##sql-ib-foo' doesn't exist in engine
DROP TABLE `#mysql50##sql-ib-foo`;
Warnings:
Warning 1932 Table 'test.#mysql50##sql-ib-foo' doesn't exist in engine

View File

@@ -8,7 +8,6 @@ SET GLOBAL innodb_monitor_enable = module_ddl;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -27,7 +26,6 @@ ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -81,7 +79,6 @@ SET DEBUG_SYNC = 'now WAIT_FOR scanned';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -99,7 +96,6 @@ SET DEBUG_SYNC = 'now WAIT_FOR created';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -113,7 +109,6 @@ ALTER TABLE t1 ADD UNIQUE INDEX(c2);
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -163,7 +158,6 @@ SET DEBUG_SYNC = 'now WAIT_FOR c2d_created';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -175,7 +169,6 @@ ERROR 70100: Query execution was interrupted
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -243,7 +236,6 @@ SET DEBUG_SYNC = 'now WAIT_FOR c2e_created';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -283,7 +275,6 @@ ROLLBACK;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -316,7 +307,6 @@ ERROR HY000: Creating index 'c2e' required more than 'innodb_online_alter_log_ma
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 1 ddl_background_drop_indexes 1
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -328,7 +318,6 @@ name pos
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 1 ddl_background_drop_indexes 1
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -338,7 +327,6 @@ ALTER TABLE t1 COMMENT 'testing if c2e will be dropped';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -363,7 +351,6 @@ SET DEBUG_SYNC = 'now WAIT_FOR c2f_created';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -387,7 +374,6 @@ ROLLBACK;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -400,7 +386,6 @@ ALTER TABLE t1 CHANGE c2 c22f INT;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -464,7 +449,6 @@ name pos
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 1 ddl_background_drop_indexes 1
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -473,7 +457,6 @@ connection default;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 1 ddl_background_drop_indexes 1
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -493,7 +476,6 @@ ALTER TABLE t1 DROP INDEX c2d, DROP INDEX c2f;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0

View File

@@ -10,7 +10,6 @@ SET GLOBAL innodb_monitor_enable = module_ddl;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -29,7 +28,6 @@ ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -95,7 +93,6 @@ SET DEBUG_SYNC = 'now WAIT_FOR scanned';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -123,7 +120,6 @@ ALTER TABLE t1 DROP INDEX c2, ADD PRIMARY KEY(c1), ALGORITHM = INPLACE;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -148,7 +144,6 @@ SET DEBUG_SYNC = 'now WAIT_FOR rebuilt';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -164,7 +159,6 @@ ERROR 70100: Query execution was interrupted
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -215,7 +209,6 @@ SET DEBUG_SYNC = 'now WAIT_FOR rebuilt2';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -247,7 +240,6 @@ ROLLBACK;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -274,7 +266,6 @@ ERROR HY000: Creating index 'PRIMARY' required more than 'innodb_online_alter_lo
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 0 ddl_sort_file_alter_table 0
@@ -304,7 +295,6 @@ SET DEBUG_SYNC = 'now WAIT_FOR rebuilt3';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 2 ddl_sort_file_alter_table 2
@@ -320,7 +310,6 @@ ROLLBACK;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 1 ddl_online_create_index 1
ddl_pending_alter_table 1 ddl_pending_alter_table 1
ddl_sort_file_alter_table 2 ddl_sort_file_alter_table 2
@@ -330,7 +319,6 @@ connection con1;
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 2 ddl_sort_file_alter_table 2
@@ -410,7 +398,6 @@ SET DEBUG_SYNC = 'now SIGNAL ddl_timed_out';
SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl';
name count name count
ddl_background_drop_indexes 0 ddl_background_drop_indexes 0
ddl_background_drop_tables 0
ddl_online_create_index 0 ddl_online_create_index 0
ddl_pending_alter_table 0 ddl_pending_alter_table 0
ddl_sort_file_alter_table 6 ddl_sort_file_alter_table 6

View File

@@ -476,6 +476,7 @@ ALTER TABLE t1 IMPORT TABLESPACE;
ERROR HY000: Index for table 't1' is corrupt; try to repair it ERROR HY000: Index for table 't1' is corrupt; try to repair it
SET SESSION debug_dbug=@saved_debug_dbug; SET SESSION debug_dbug=@saved_debug_dbug;
restore: t1 .ibd and .cfg files restore: t1 .ibd and .cfg files
ALTER TABLE t1 IMPORT TABLESPACE;
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 ( CREATE TABLE t1 (
c1 BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, c1 BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,

View File

@@ -1,41 +1,22 @@
DROP TABLE if exists t1;
select @@global.innodb_stats_persistent; select @@global.innodb_stats_persistent;
@@global.innodb_stats_persistent @@global.innodb_stats_persistent
0 0
set global innodb_defragment_stats_accuracy = 20; set global innodb_defragment_stats_accuracy = 20;
# Create table. CREATE TABLE t1(a INT PRIMARY KEY, b VARCHAR(256), KEY SECOND(a, b))
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), KEY SECOND(a, b)) ENGINE=INNODB; ENGINE=INNODB;
# Populate data INSERT INTO t1 SELECT seq, REPEAT('A', 256) FROM seq_1_to_1024;
INSERT INTO t1 VALUES(1, REPEAT('A', 256));
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
# Not enough page splits to trigger persistent stats write yet. # Not enough page splits to trigger persistent stats write yet.
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select * from mysql.innodb_index_stats where table_name='t1'
count(stat_value) = 0 and stat_name in ('n_page_split','n_pages_freed,n_leaf_pages_defrag');
1 database_name table_name index_name last_update stat_name stat_value sample_size stat_description
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); INSERT INTO t1 SELECT seq, REPEAT('A', 256) FROM seq_1025_to_2048;
count(stat_value) = 0
1
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) = 0
1
INSERT INTO t1 (b) SELECT b from t1;
# Persistent stats recorded. # Persistent stats recorded.
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_page_split');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); select * from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_pages_freed');
count(stat_value) = 0 database_name table_name index_name last_update stat_name stat_value sample_size stat_description
1 select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_leaf_pages_defrag');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) > 0 count(stat_value) > 0
1 1
# Delete some rows. # Delete some rows.
@@ -62,13 +43,12 @@ delete from t1 where a between 100 * 1 and 100 * 1 + 30;
# restart # restart
# Server Restarted # Server Restarted
# Confirm persistent stats still there after restart. # Confirm persistent stats still there after restart.
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_page_split');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); select * from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_pages_freed');
count(stat_value) = 0 database_name table_name index_name last_update stat_name stat_value sample_size stat_description
1 select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_leaf_pages_defrag');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) > 0 count(stat_value) > 0
1 1
optimize table t1; optimize table t1;
@@ -77,101 +57,65 @@ test.t1 optimize status OK
select sleep(2); select sleep(2);
sleep(2) sleep(2)
0 0
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_page_split');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_pages_freed');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) > 0 count(stat_value) > 0
1 1
set global innodb_defragment_stats_accuracy = 40; set global innodb_defragment_stats_accuracy = 40;
INSERT INTO t1 (b) SELECT b from t1; INSERT INTO t1 SELECT seq, REPEAT('A', 256) FROM seq_2049_to_4096;
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_page_split');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_pages_freed');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) > 0 count(stat_value) > 0
1 1
INSERT INTO t1 (b) SELECT b from t1; INSERT INTO t1 SELECT seq, REPEAT('A', 256) FROM seq_4097_to_8192;
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_page_split');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_pages_freed');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) > 0 count(stat_value) > 0
1 1
# Table rename should cause stats rename. # Table rename should cause stats rename.
rename table t1 to t2; rename table t1 to t2;
select sleep(1); select * from mysql.innodb_index_stats where table_name = 't1';
sleep(1) database_name table_name index_name last_update stat_name stat_value sample_size stat_description
0 select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_page_split');
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split');
count(stat_value) = 0
1
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed');
count(stat_value) = 0
1
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) = 0
1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_page_split');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_pages_freed');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_leaf_pages_defrag'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) > 0 count(stat_value) > 0
1 1
# Drop index should cause stats drop. # Drop index should cause stats drop.
drop index SECOND on t2; drop index SECOND on t2;
select sleep(3); select * from mysql.innodb_index_stats where table_name = 't2' and index_name = 'SECOND';
sleep(3) database_name table_name index_name last_update stat_name stat_value sample_size stat_description
0
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and index_name = 'SECOND' and stat_name in ('n_page_split');
count(stat_value) > 0
1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and index_name = 'SECOND' and stat_name in ('n_pages_freed');
count(stat_value) > 0
1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and index_name = 'SECOND' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) > 0
1
# restart # restart
Server Restarted Server Restarted
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_page_split');
count(stat_value) = 0
1
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed');
count(stat_value) = 0
1
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) = 0
1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_page_split');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_pages_freed');
count(stat_value) > 0 count(stat_value) > 0
1 1
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_leaf_pages_defrag'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) > 0 count(stat_value) > 0
1 1
# Clean up # Clean up
DROP TABLE t2; DROP TABLE t2;
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_page_split'); select * from mysql.innodb_index_stats where table_name = 't2';
count(stat_value) = 0 database_name table_name index_name last_update stat_name stat_value sample_size stat_description
1
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed');
count(stat_value) = 0
1
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_leaf_pages_defrag');
count(stat_value) = 0
1

View File

@@ -228,7 +228,6 @@ innodb_master_thread_sleeps server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL N
innodb_activity_count server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Current server activity count innodb_activity_count server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Current server activity count
innodb_master_active_loops server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of times master thread performs its tasks when server is active innodb_master_active_loops server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of times master thread performs its tasks when server is active
innodb_master_idle_loops server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of times master thread performs its tasks when server is idle innodb_master_idle_loops server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of times master thread performs its tasks when server is idle
innodb_background_drop_table_usec server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Time (in microseconds) spent to process drop table list
innodb_log_flush_usec server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Time (in microseconds) spent to flush log records innodb_log_flush_usec server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Time (in microseconds) spent to flush log records
innodb_dict_lru_usec server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Time (in microseconds) spent to process DICT LRU list innodb_dict_lru_usec server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Time (in microseconds) spent to process DICT LRU list
innodb_dict_lru_count_active server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of tables evicted from DICT LRU list in the active loop innodb_dict_lru_count_active server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of tables evicted from DICT LRU list in the active loop
@@ -245,7 +244,6 @@ dml_system_inserts dml 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 s
dml_system_deletes dml 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Number of system rows deleted dml_system_deletes dml 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Number of system rows deleted
dml_system_updates dml 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Number of system rows updated dml_system_updates dml 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Number of system rows updated
ddl_background_drop_indexes ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of indexes waiting to be dropped after failed index creation ddl_background_drop_indexes ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of indexes waiting to be dropped after failed index creation
ddl_background_drop_tables ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of tables in background drop table list
ddl_online_create_index ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of indexes being created online ddl_online_create_index ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of indexes being created online
ddl_pending_alter_table ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of ALTER TABLE, CREATE INDEX, DROP INDEX in progress ddl_pending_alter_table ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of ALTER TABLE, CREATE INDEX, DROP INDEX in progress
ddl_sort_file_alter_table ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of sort files created during alter table ddl_sort_file_alter_table ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of sort files created during alter table

View File

@@ -1,7 +1,14 @@
CREATE DATABASE unlocked;
CREATE TABLE unlocked.t1(a INT PRIMARY KEY) ENGINE=INNODB STATS_PERSISTENT=0;
CREATE DATABASE locked;
CREATE TABLE locked.t1(a INT PRIMARY KEY) ENGINE=INNODB STATS_PERSISTENT=1;
CREATE TABLE innodb_stats_drop_locked (c INT, KEY c_key (c))
ENGINE=INNODB STATS_PERSISTENT=1;
ANALYZE TABLE innodb_stats_drop_locked;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.innodb_stats_drop_locked analyze status Engine-independent statistics collected test.innodb_stats_drop_locked analyze status Engine-independent statistics collected
test.innodb_stats_drop_locked analyze status OK test.innodb_stats_drop_locked analyze status OK
SET autocommit=0; BEGIN;
SELECT table_name FROM mysql.innodb_table_stats SELECT table_name FROM mysql.innodb_table_stats
WHERE table_name='innodb_stats_drop_locked' WHERE table_name='innodb_stats_drop_locked'
FOR UPDATE; FOR UPDATE;
@@ -19,21 +26,23 @@ innodb_stats_drop_locked
innodb_stats_drop_locked innodb_stats_drop_locked
innodb_stats_drop_locked innodb_stats_drop_locked
connect con1,localhost,root,,; connect con1,localhost,root,,;
connection con1;
ALTER TABLE innodb_stats_drop_locked DROP INDEX c_key; ALTER TABLE innodb_stats_drop_locked DROP INDEX c_key;
Warnings: ERROR HY000: Lock wait timeout exceeded; try restarting transaction
Warning 1205 Unable to delete statistics for index c_key from mysql.innodb_index_stats because the rows are locked: Lock wait timeout. They can be deleted later using DELETE FROM mysql.innodb_index_stats WHERE database_name = 'test' AND table_name = 'innodb_stats_drop_locked' AND index_name = 'c_key';
SHOW CREATE TABLE innodb_stats_drop_locked; SHOW CREATE TABLE innodb_stats_drop_locked;
Table Create Table Table Create Table
innodb_stats_drop_locked CREATE TABLE `innodb_stats_drop_locked` ( innodb_stats_drop_locked CREATE TABLE `innodb_stats_drop_locked` (
`c` int(11) DEFAULT NULL `c` int(11) DEFAULT NULL,
KEY `c_key` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 STATS_PERSISTENT=1 ) ENGINE=InnoDB DEFAULT CHARSET=latin1 STATS_PERSISTENT=1
DROP TABLE innodb_stats_drop_locked; DROP TABLE innodb_stats_drop_locked;
SHOW TABLES; ERROR HY000: Lock wait timeout exceeded; try restarting transaction
Tables_in_test DROP DATABASE unlocked;
connection default; DROP DATABASE locked;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
disconnect con1; disconnect con1;
connection default;
COMMIT; COMMIT;
DROP DATABASE locked;
SELECT table_name FROM mysql.innodb_table_stats SELECT table_name FROM mysql.innodb_table_stats
WHERE table_name='innodb_stats_drop_locked'; WHERE table_name='innodb_stats_drop_locked';
table_name table_name
@@ -48,5 +57,17 @@ innodb_stats_drop_locked
innodb_stats_drop_locked innodb_stats_drop_locked
innodb_stats_drop_locked innodb_stats_drop_locked
innodb_stats_drop_locked innodb_stats_drop_locked
DELETE FROM mysql.innodb_index_stats WHERE database_name='test' AND table_name='innodb_stats_drop_locked'; ALTER TABLE innodb_stats_drop_locked DROP INDEX c_key;
DELETE FROM mysql.innodb_table_stats WHERE database_name='test' AND table_name='innodb_stats_drop_locked'; SELECT table_name FROM mysql.innodb_index_stats
WHERE table_name='innodb_stats_drop_locked';
table_name
innodb_stats_drop_locked
innodb_stats_drop_locked
innodb_stats_drop_locked
DROP TABLE innodb_stats_drop_locked;
SELECT table_name FROM mysql.innodb_table_stats
WHERE table_name='innodb_stats_drop_locked';
table_name
SELECT table_name FROM mysql.innodb_index_stats
WHERE table_name='innodb_stats_drop_locked';
table_name

View File

@@ -1,5 +1,6 @@
CREATE TABLE stats_rename1 (a INT, PRIMARY KEY (a)) CREATE TABLE stats_rename1 (a INT PRIMARY KEY, b INT UNIQUE)
ENGINE=INNODB STATS_PERSISTENT=1; ENGINE=INNODB STATS_PERSISTENT=1;
BEGIN;
INSERT INTO mysql.innodb_table_stats INSERT INTO mysql.innodb_table_stats
SELECT SELECT
database_name, database_name,
@@ -22,6 +23,7 @@ sample_size,
stat_description stat_description
FROM mysql.innodb_index_stats FROM mysql.innodb_index_stats
WHERE table_name = 'stats_rename1'; WHERE table_name = 'stats_rename1';
COMMIT;
SELECT table_name, n_rows SELECT table_name, n_rows
FROM mysql.innodb_table_stats FROM mysql.innodb_table_stats
WHERE table_name IN ('stats_rename1', 'stats_rename2'); WHERE table_name IN ('stats_rename1', 'stats_rename2');
@@ -44,6 +46,18 @@ table_name stats_rename1
index_name PRIMARY index_name PRIMARY
stat_name size stat_name size
stat_value 1 stat_value 1
table_name stats_rename1
index_name b
stat_name n_diff_pfx01
stat_value 0
table_name stats_rename1
index_name b
stat_name n_leaf_pages
stat_value 1
table_name stats_rename1
index_name b
stat_name size
stat_value 1
table_name stats_rename2 table_name stats_rename2
index_name PRIMARY index_name PRIMARY
stat_name n_diff_pfx01 stat_name n_diff_pfx01
@@ -56,7 +70,32 @@ table_name stats_rename2
index_name PRIMARY index_name PRIMARY
stat_name size stat_name size
stat_value 567 stat_value 567
table_name stats_rename2
index_name b
stat_name n_diff_pfx01
stat_value 567
table_name stats_rename2
index_name b
stat_name n_leaf_pages
stat_value 567
table_name stats_rename2
index_name b
stat_name size
stat_value 567
RENAME TABLE stats_rename1 TO stats_rename2; RENAME TABLE stats_rename1 TO stats_rename2;
ERROR 23000: Can't write; duplicate key in table 'mysql.innodb_table_stats'
BEGIN;
DELETE FROM mysql.innodb_table_stats WHERE table_name='stats_rename2';
DELETE FROM mysql.innodb_index_stats WHERE table_name='stats_rename2';
COMMIT;
RENAME TABLE stats_rename1 TO stats_rename2;
UPDATE mysql.innodb_index_stats SET index_name='c'
WHERE table_name='stats_rename2' AND index_name='PRIMARY';
ALTER TABLE stats_rename2 CHANGE b d INT, RENAME INDEX b TO c;
ERROR 23000: Can't write; duplicate key in table 'mysql.innodb_index_stats'
UPDATE mysql.innodb_index_stats SET index_name='PRIMARY'
WHERE table_name='stats_rename2' AND index_name='c';
ALTER TABLE stats_rename2 CHANGE b d INT, RENAME INDEX b TO c;
SELECT table_name, n_rows SELECT table_name, n_rows
FROM mysql.innodb_table_stats FROM mysql.innodb_table_stats
WHERE table_name IN ('stats_rename1', 'stats_rename2'); WHERE table_name IN ('stats_rename1', 'stats_rename2');
@@ -77,4 +116,16 @@ table_name stats_rename2
index_name PRIMARY index_name PRIMARY
stat_name size stat_name size
stat_value 1 stat_value 1
table_name stats_rename2
index_name c
stat_name n_diff_pfx01
stat_value 0
table_name stats_rename2
index_name c
stat_name n_leaf_pages
stat_value 1
table_name stats_rename2
index_name c
stat_name size
stat_value 1
DROP TABLE stats_rename2; DROP TABLE stats_rename2;

View File

@@ -74,6 +74,8 @@ DROP TABLE t2,t3;
CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB; CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB;
ERROR HY000: Can't create table `test`.`t0` (errno: 184 "Tablespace already exists") ERROR HY000: Can't create table `test`.`t0` (errno: 184 "Tablespace already exists")
CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB; CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB;
ERROR HY000: Can't create table `test`.`t0` (errno: 184 "Tablespace already exists")
CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB;
DROP TABLE t0; DROP TABLE t0;
CREATE TABLE u1(a INT PRIMARY KEY) ENGINE=InnoDB; CREATE TABLE u1(a INT PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE u2(a INT PRIMARY KEY) ENGINE=InnoDB; CREATE TABLE u2(a INT PRIMARY KEY) ENGINE=InnoDB;

View File

@@ -194,7 +194,6 @@ innodb_master_thread_sleeps disabled
innodb_activity_count disabled innodb_activity_count disabled
innodb_master_active_loops disabled innodb_master_active_loops disabled
innodb_master_idle_loops disabled innodb_master_idle_loops disabled
innodb_background_drop_table_usec disabled
innodb_log_flush_usec disabled innodb_log_flush_usec disabled
innodb_dict_lru_usec disabled innodb_dict_lru_usec disabled
innodb_dict_lru_count_active disabled innodb_dict_lru_count_active disabled
@@ -211,7 +210,6 @@ dml_system_inserts disabled
dml_system_deletes disabled dml_system_deletes disabled
dml_system_updates disabled dml_system_updates disabled
ddl_background_drop_indexes disabled ddl_background_drop_indexes disabled
ddl_background_drop_tables disabled
ddl_online_create_index disabled ddl_online_create_index disabled
ddl_pending_alter_table disabled ddl_pending_alter_table disabled
ddl_sort_file_alter_table disabled ddl_sort_file_alter_table disabled

View File

@@ -1,31 +0,0 @@
FLUSH TABLES;
CREATE TABLE t1 (a SERIAL, b INT, c INT, d INT) ENGINE=InnoDB;
INSERT INTO t1 () VALUES ();
connect con1,localhost,root,,test;
SET DEBUG_SYNC='before_rename_table_commit SIGNAL renamed WAIT_FOR ever';
RENAME TABLE t1 TO t2;
connection default;
SET DEBUG_SYNC='now WAIT_FOR renamed';
# restart
disconnect con1;
SELECT * FROM t1;
a b c d
1 NULL NULL NULL
CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
BEGIN;
INSERT INTO t2 VALUES(1);
connect con1,localhost,root,,test;
SET DEBUG_SYNC='innodb_rename_in_cache SIGNAL committed WAIT_FOR ever';
RENAME TABLE t1 TO t3;
connection default;
SET DEBUG_SYNC='now WAIT_FOR committed';
COMMIT;
# restart
disconnect con1;
SELECT * FROM t1;
a b c d
1 NULL NULL NULL
SELECT * FROM t2;
a
1
DROP TABLE t1,t2;

View File

@@ -50,3 +50,13 @@ Warnings:
Warning 1814 Tablespace has been discarded for table `u` Warning 1814 Tablespace has been discarded for table `u`
TRUNCATE u; TRUNCATE u;
DROP TABLE u; DROP TABLE u;
#
# Test for a regression found during MDEV-25506 rewrite of DROP
#
CREATE TEMPORARY TABLE t1 (a INT) ENGINE=InnoDB;
LOCK TABLE t1 READ;
TRUNCATE TABLE t1;
TRUNCATE TABLE t1;
UNLOCK TABLES;
DROP TEMPORARY TABLE t1;
# End of 10.6 tests

View File

@@ -18,8 +18,9 @@ disconnect con2;
connect con1,localhost,root; connect con1,localhost,root;
SELECT * FROM t1 LOCK IN SHARE MODE; SELECT * FROM t1 LOCK IN SHARE MODE;
connection default; connection default;
SET innodb_lock_wait_timeout=1;
DROP TABLE t2; DROP TABLE t2;
# restart ERROR HY000: Lock wait timeout exceeded; try restarting transaction
disconnect con1; disconnect con1;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT * FROM t1; SELECT * FROM t1;
@@ -30,6 +31,9 @@ SELECT * FROM t1;
a a
1 1
DROP TABLE t1; DROP TABLE t1;
DROP TABLE t2;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
XA ROLLBACK 'y';
DROP TABLE t2;
SET GLOBAL innodb_fast_shutdown=0; SET GLOBAL innodb_fast_shutdown=0;
# restart # restart
XA ROLLBACK 'y';

View File

@@ -46,16 +46,15 @@ CREATE TABLE tab(a INT) ENGINE=InnoDB;
# Remove the *.ibd file # Remove the *.ibd file
ALTER TABLE tab DISCARD TABLESPACE; ALTER TABLE tab DISCARD TABLESPACE;
# Move the *.ibd,*.cfg file into orginal location # Move the *.ibd,*.cfg file into original location
--move_file $MYSQLD_DATADIR/tab.cfg $MYSQLD_DATADIR/test/tab.cfg --move_file $MYSQLD_DATADIR/tab.cfg $MYSQLD_DATADIR/test/tab.cfg
--move_file $MYSQLD_DATADIR/tab.ibd $MYSQLD_DATADIR/test/tab.ibd --move_file $MYSQLD_DATADIR/tab.ibd $MYSQLD_DATADIR/test/tab.ibd
--error ER_TABLE_SCHEMA_MISMATCH --error ER_TABLE_SCHEMA_MISMATCH
ALTER TABLE tab IMPORT TABLESPACE; ALTER TABLE tab IMPORT TABLESPACE;
# Take the backup of the ibd and cfg files --move_file $MYSQLD_DATADIR/test/tab.cfg $MYSQLD_DATADIR/tab.cfg
--copy_file $MYSQLD_DATADIR/test/tab.cfg $MYSQLD_DATADIR/tab.cfg --move_file $MYSQLD_DATADIR/test/tab.ibd $MYSQLD_DATADIR/tab.ibd
--copy_file $MYSQLD_DATADIR/test/tab.ibd $MYSQLD_DATADIR/tab.ibd
# Cleanup # Cleanup
DROP TABLE tab; DROP TABLE tab;

View File

@@ -1,48 +0,0 @@
--source include/have_innodb.inc
--source include/have_debug.inc
# Embedded server does not support restarting
--source include/not_embedded.inc
let $MYSQLD_DATADIR=`select @@datadir`;
CREATE TABLE t(c0 SERIAL, c1 INT, c2 INT, c3 INT, c4 INT,
KEY(c1), KEY(c2), KEY(c2,c1),
KEY(c3), KEY(c3,c1), KEY(c3,c2), KEY(c3,c2,c1),
KEY(c4), KEY(c4,c1), KEY(c4,c2), KEY(c4,c2,c1),
KEY(c4,c3), KEY(c4,c3,c1), KEY(c4,c3,c2), KEY(c4,c3,c2,c1)) ENGINE=InnoDB;
CREATE TABLE `#mysql50##sql-ib-foo`(a SERIAL) ENGINE=InnoDB;
INSERT INTO t (c1) VALUES (1),(2),(1);
let $n= 10;
SET DEBUG_DBUG='+d,row_drop_table_add_to_background';
--disable_query_log
let $i= $n;
while ($i) {
eval CREATE TABLE t$i LIKE t;
dec $i;
}
let $i= $n;
while ($i) {
eval DROP TABLE t$i;
dec $i;
}
--enable_query_log
--error ER_DUP_ENTRY
CREATE TABLE target (PRIMARY KEY(c1)) ENGINE=InnoDB SELECT * FROM t;
--error ER_NO_SUCH_TABLE
SELECT * from target;
DROP TABLE t;
--source include/shutdown_mysqld.inc
--remove_files_wildcard $MYSQLD_DATADIR/test #sql-*.ibd
--source include/start_mysqld.inc
CREATE TABLE t (a INT) ENGINE=InnoDB;
DROP TABLE t;
--error ER_BAD_TABLE_ERROR
DROP TABLE target;
CREATE TABLE target (a INT) ENGINE=InnoDB;
DROP TABLE target;
--error ER_NO_SUCH_TABLE_IN_ENGINE
SELECT * FROM `#mysql50##sql-ib-foo`;
DROP TABLE `#mysql50##sql-ib-foo`;

View File

@@ -1014,6 +1014,7 @@ do "$ENV{MTR_SUITE_DIR}/include/innodb-util.pl";
ib_restore_tablespaces("test", "t1"); ib_restore_tablespaces("test", "t1");
EOF EOF
ALTER TABLE t1 IMPORT TABLESPACE;
DROP TABLE t1; DROP TABLE t1;
# #

View File

@@ -96,6 +96,10 @@ if ($checksum_algorithm == "strict_full_crc32") {
ALTER TABLE t2 IMPORT TABLESPACE; ALTER TABLE t2 IMPORT TABLESPACE;
DROP TABLE t2; DROP TABLE t2;
if ($error_code) {
--remove_file $MYSQLD_DATADIR/test/t2.ibd
}
SET GLOBAL innodb_file_per_table = 1; SET GLOBAL innodb_file_per_table = 1;
SELECT @@innodb_file_per_table; SELECT @@innodb_file_per_table;

View File

@@ -6,7 +6,7 @@
--source include/have_innodb.inc --source include/have_innodb.inc
SET @innodb_file_per_table_orig=@@GLOBAL.innodb_file_per_table; SET @innodb_file_per_table_orig=@@GLOBAL.innodb_file_per_table;
LET $regexp=/FTS_[0-9a-f_]+([A-Z0-9_]+)\.([islbd]{3})/FTS_AUX_\1.\2/ /#sql-ib[1-9][0-9]*\.ibd\n//; LET $regexp=/FTS_[0-9a-f_]+([A-Z0-9_]+)\.([islbd]{3})/FTS_AUX_\1.\2/;
# Set up some variables # Set up some variables
LET $MYSQL_DATA_DIR = `select @@datadir`; LET $MYSQL_DATA_DIR = `select @@datadir`;

View File

@@ -1,46 +1,26 @@
--source include/have_innodb.inc --source include/have_innodb.inc
--source include/have_sequence.inc
--source include/big_test.inc --source include/big_test.inc
--source include/not_valgrind.inc --source include/not_valgrind.inc
--source include/not_embedded.inc --source include/not_embedded.inc
--disable_warnings
DROP TABLE if exists t1;
--enable_warnings
--disable_query_log
let $innodb_defragment_stats_accuracy_orig=`select @@innodb_defragment_stats_accuracy`;
--enable_query_log
select @@global.innodb_stats_persistent; select @@global.innodb_stats_persistent;
set global innodb_defragment_stats_accuracy = 20; set global innodb_defragment_stats_accuracy = 20;
--echo # Create table. CREATE TABLE t1(a INT PRIMARY KEY, b VARCHAR(256), KEY SECOND(a, b))
CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY AUTO_INCREMENT, b VARCHAR(256), KEY SECOND(a, b)) ENGINE=INNODB; ENGINE=INNODB;
INSERT INTO t1 SELECT seq, REPEAT('A', 256) FROM seq_1_to_1024;
--echo # Populate data
INSERT INTO t1 VALUES(1, REPEAT('A', 256));
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
INSERT INTO t1 (b) SELECT b from t1;
--echo # Not enough page splits to trigger persistent stats write yet. --echo # Not enough page splits to trigger persistent stats write yet.
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select * from mysql.innodb_index_stats where table_name='t1'
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); and stat_name in ('n_page_split','n_pages_freed,n_leaf_pages_defrag');
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
INSERT INTO t1 (b) SELECT b from t1; INSERT INTO t1 SELECT seq, REPEAT('A', 256) FROM seq_1025_to_2048;
--echo # Persistent stats recorded. --echo # Persistent stats recorded.
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_page_split');
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); select * from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_leaf_pages_defrag');
--echo # Delete some rows. --echo # Delete some rows.
let $num_delete = 20; let $num_delete = 20;
@@ -55,71 +35,53 @@ while ($num_delete)
--echo # Server Restarted --echo # Server Restarted
--echo # Confirm persistent stats still there after restart. --echo # Confirm persistent stats still there after restart.
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_page_split');
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); select * from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_leaf_pages_defrag');
optimize table t1; optimize table t1;
select sleep(2); select sleep(2);
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_page_split');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_leaf_pages_defrag');
set global innodb_defragment_stats_accuracy = 40; set global innodb_defragment_stats_accuracy = 40;
INSERT INTO t1 (b) SELECT b from t1; INSERT INTO t1 SELECT seq, REPEAT('A', 256) FROM seq_2049_to_4096;
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_page_split');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_leaf_pages_defrag');
INSERT INTO t1 SELECT seq, REPEAT('A', 256) FROM seq_4097_to_8192;
INSERT INTO t1 (b) SELECT b from t1; select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_page_split');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't1' and stat_name in ('n_leaf_pages_defrag');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag');
--echo # Table rename should cause stats rename. --echo # Table rename should cause stats rename.
rename table t1 to t2; rename table t1 to t2;
select sleep(1); select * from mysql.innodb_index_stats where table_name = 't1';
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_page_split');
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_pages_freed');
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_leaf_pages_defrag');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_page_split');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_leaf_pages_defrag');
--echo # Drop index should cause stats drop. --echo # Drop index should cause stats drop.
drop index SECOND on t2; drop index SECOND on t2;
select sleep(3);
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and index_name = 'SECOND' and stat_name in ('n_page_split'); select * from mysql.innodb_index_stats where table_name = 't2' and index_name = 'SECOND';
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and index_name = 'SECOND' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and index_name = 'SECOND' and stat_name in ('n_leaf_pages_defrag');
--source include/restart_mysqld.inc --source include/restart_mysqld.inc
--echo Server Restarted --echo Server Restarted
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_page_split'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_page_split');
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_pages_freed'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_pages_freed');
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t1%' and stat_name in ('n_leaf_pages_defrag'); select count(stat_value) > 0 from mysql.innodb_index_stats where table_name = 't2' and stat_name in ('n_leaf_pages_defrag');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_page_split');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed');
select count(stat_value) > 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_leaf_pages_defrag');
--echo # Clean up --echo # Clean up
DROP TABLE t2; DROP TABLE t2;
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_page_split'); select * from mysql.innodb_index_stats where table_name = 't2';
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_pages_freed');
select count(stat_value) = 0 from mysql.innodb_index_stats where table_name like '%t2%' and stat_name in ('n_leaf_pages_defrag');
--disable_query_log
EVAL SET GLOBAL innodb_defragment_stats_accuracy = $innodb_defragment_stats_accuracy_orig;
--enable_query_log

View File

@@ -5,49 +5,42 @@
-- source include/have_innodb.inc -- source include/have_innodb.inc
-- disable_warnings CREATE DATABASE unlocked;
-- disable_query_log CREATE TABLE unlocked.t1(a INT PRIMARY KEY) ENGINE=INNODB STATS_PERSISTENT=0;
CREATE DATABASE locked;
DROP TABLE IF EXISTS innodb_stats_drop_locked; CREATE TABLE locked.t1(a INT PRIMARY KEY) ENGINE=INNODB STATS_PERSISTENT=1;
CREATE TABLE innodb_stats_drop_locked (c INT, KEY c_key (c)) CREATE TABLE innodb_stats_drop_locked (c INT, KEY c_key (c))
ENGINE=INNODB STATS_PERSISTENT=1; ENGINE=INNODB STATS_PERSISTENT=1;
ANALYZE TABLE innodb_stats_drop_locked; ANALYZE TABLE innodb_stats_drop_locked;
-- enable_warnings BEGIN;
-- enable_query_log
SET autocommit=0;
SELECT table_name FROM mysql.innodb_table_stats SELECT table_name FROM mysql.innodb_table_stats
WHERE table_name='innodb_stats_drop_locked' WHERE table_name='innodb_stats_drop_locked'
FOR UPDATE; FOR UPDATE;
SELECT table_name FROM mysql.innodb_index_stats SELECT table_name FROM mysql.innodb_index_stats
WHERE table_name='innodb_stats_drop_locked' WHERE table_name='innodb_stats_drop_locked'
FOR UPDATE; FOR UPDATE;
-- connect (con1,localhost,root,,) -- connect (con1,localhost,root,,)
--error ER_LOCK_WAIT_TIMEOUT
-- connection con1
ALTER TABLE innodb_stats_drop_locked DROP INDEX c_key; ALTER TABLE innodb_stats_drop_locked DROP INDEX c_key;
# the index should be gone # the index should be gone
SHOW CREATE TABLE innodb_stats_drop_locked; SHOW CREATE TABLE innodb_stats_drop_locked;
--error ER_LOCK_WAIT_TIMEOUT
DROP TABLE innodb_stats_drop_locked; DROP TABLE innodb_stats_drop_locked;
# the table should be gone DROP DATABASE unlocked;
SHOW TABLES; --error ER_LOCK_WAIT_TIMEOUT
DROP DATABASE locked;
-- connection default
-- disconnect con1 -- disconnect con1
-- connection default
COMMIT; COMMIT;
DROP DATABASE locked;
# the stats should be there # the stats should be there
SELECT table_name FROM mysql.innodb_table_stats SELECT table_name FROM mysql.innodb_table_stats
@@ -56,8 +49,15 @@ WHERE table_name='innodb_stats_drop_locked';
SELECT table_name FROM mysql.innodb_index_stats SELECT table_name FROM mysql.innodb_index_stats
WHERE table_name='innodb_stats_drop_locked'; WHERE table_name='innodb_stats_drop_locked';
DELETE FROM mysql.innodb_index_stats WHERE database_name='test' AND table_name='innodb_stats_drop_locked'; ALTER TABLE innodb_stats_drop_locked DROP INDEX c_key;
DELETE FROM mysql.innodb_table_stats WHERE database_name='test' AND table_name='innodb_stats_drop_locked';
--disable_query_log SELECT table_name FROM mysql.innodb_index_stats
call mtr.add_suppression("Unable to delete statistics for table test.innodb_stats_drop_locked: Lock wait timeout. They can be deleted later using DELETE FROM mysql.innodb_index_stats WHERE database_name"); WHERE table_name='innodb_stats_drop_locked';
--enable_query_log
DROP TABLE innodb_stats_drop_locked;
SELECT table_name FROM mysql.innodb_table_stats
WHERE table_name='innodb_stats_drop_locked';
SELECT table_name FROM mysql.innodb_index_stats
WHERE table_name='innodb_stats_drop_locked';

View File

@@ -10,9 +10,10 @@
-- vertical_results -- vertical_results
CREATE TABLE stats_rename1 (a INT, PRIMARY KEY (a)) CREATE TABLE stats_rename1 (a INT PRIMARY KEY, b INT UNIQUE)
ENGINE=INNODB STATS_PERSISTENT=1; ENGINE=INNODB STATS_PERSISTENT=1;
BEGIN;
INSERT INTO mysql.innodb_table_stats INSERT INTO mysql.innodb_table_stats
SELECT SELECT
database_name, database_name,
@@ -36,6 +37,7 @@ sample_size,
stat_description stat_description
FROM mysql.innodb_index_stats FROM mysql.innodb_index_stats
WHERE table_name = 'stats_rename1'; WHERE table_name = 'stats_rename1';
COMMIT;
SELECT table_name, n_rows SELECT table_name, n_rows
FROM mysql.innodb_table_stats FROM mysql.innodb_table_stats
@@ -45,7 +47,22 @@ SELECT table_name, index_name, stat_name, stat_value
FROM mysql.innodb_index_stats FROM mysql.innodb_index_stats
WHERE table_name IN ('stats_rename1', 'stats_rename2'); WHERE table_name IN ('stats_rename1', 'stats_rename2');
--error ER_DUP_KEY
RENAME TABLE stats_rename1 TO stats_rename2; RENAME TABLE stats_rename1 TO stats_rename2;
BEGIN;
DELETE FROM mysql.innodb_table_stats WHERE table_name='stats_rename2';
DELETE FROM mysql.innodb_index_stats WHERE table_name='stats_rename2';
COMMIT;
RENAME TABLE stats_rename1 TO stats_rename2;
UPDATE mysql.innodb_index_stats SET index_name='c'
WHERE table_name='stats_rename2' AND index_name='PRIMARY';
--error ER_DUP_KEY
ALTER TABLE stats_rename2 CHANGE b d INT, RENAME INDEX b TO c;
UPDATE mysql.innodb_index_stats SET index_name='PRIMARY'
WHERE table_name='stats_rename2' AND index_name='c';
ALTER TABLE stats_rename2 CHANGE b d INT, RENAME INDEX b TO c;
SELECT table_name, n_rows SELECT table_name, n_rows
FROM mysql.innodb_table_stats FROM mysql.innodb_table_stats

View File

@@ -79,6 +79,7 @@ select * from t1;
alter table t1 import tablespace; alter table t1 import tablespace;
--error ER_TABLESPACE_DISCARDED --error ER_TABLESPACE_DISCARDED
select * from t1; select * from t1;
--remove_file $MYSQLD_DATADIR/test/t1.ibd
drop table t2; drop table t2;
drop table t1; drop table t1;

View File

@@ -137,7 +137,9 @@ DROP TABLE t2,t3;
--error ER_CANT_CREATE_TABLE --error ER_CANT_CREATE_TABLE
CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB; CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB;
--error ER_CANT_CREATE_TABLE
CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB;
--remove_file $MYSQLD_DATADIR/test/t0.ibd
CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB; CREATE TABLE t0(a INT PRIMARY KEY) ENGINE=InnoDB;
DROP TABLE t0; DROP TABLE t0;

View File

@@ -1,41 +0,0 @@
--source include/have_innodb.inc
--source include/have_debug.inc
--source include/have_debug_sync.inc
--source include/not_embedded.inc
FLUSH TABLES;
LET $datadir= `SELECT @@datadir`;
CREATE TABLE t1 (a SERIAL, b INT, c INT, d INT) ENGINE=InnoDB;
INSERT INTO t1 () VALUES ();
--connect (con1,localhost,root,,test)
SET DEBUG_SYNC='before_rename_table_commit SIGNAL renamed WAIT_FOR ever';
--send
RENAME TABLE t1 TO t2;
--connection default
SET DEBUG_SYNC='now WAIT_FOR renamed';
--let $shutdown_timeout=0
--source include/restart_mysqld.inc
--disconnect con1
SELECT * FROM t1;
CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
BEGIN;
INSERT INTO t2 VALUES(1);
--connect (con1,localhost,root,,test)
SET DEBUG_SYNC='innodb_rename_in_cache SIGNAL committed WAIT_FOR ever';
--send
RENAME TABLE t1 TO t3;
--connection default
SET DEBUG_SYNC='now WAIT_FOR committed';
COMMIT;
--let $shutdown_timeout=0
--source include/restart_mysqld.inc
--disconnect con1
SELECT * FROM t1;
SELECT * FROM t2;
DROP TABLE t1,t2;

View File

@@ -1,7 +1,6 @@
--source include/innodb_page_size.inc --source include/innodb_page_size.inc
# Embedded server tests do not support restarting # Embedded server tests do not support restarting
--source include/not_embedded.inc --source include/not_embedded.inc
--source include/maybe_debug.inc
--disable_query_log --disable_query_log
call mtr.add_suppression("InnoDB: Table `mysql`\\.`innodb_table_stats` not found"); call mtr.add_suppression("InnoDB: Table `mysql`\\.`innodb_table_stats` not found");
@@ -31,9 +30,6 @@ let bugdir= $MYSQLTEST_VARDIR/tmp/table_flags;
--let $d=$d --innodb-undo-tablespaces=0 --let $d=$d --innodb-undo-tablespaces=0
--let $d=$d --innodb-purge-rseg-truncate-frequency=1 --let $d=$d --innodb-purge-rseg-truncate-frequency=1
--let $d=$d --skip-innodb-fast-shutdown --let $d=$d --skip-innodb-fast-shutdown
if ($have_debug) {
--let $d=$d --debug=d,create_and_drop_garbage
}
--let $restart_noprint=1 --let $restart_noprint=1
--let $restart_parameters=$d --innodb-stats-persistent=0 --let $restart_parameters=$d --innodb-stats-persistent=0
--source include/restart_mysqld.inc --source include/restart_mysqld.inc

View File

@@ -67,3 +67,15 @@ RENAME TABLE t TO u;
TRUNCATE u; TRUNCATE u;
TRUNCATE u; TRUNCATE u;
DROP TABLE u; DROP TABLE u;
--echo #
--echo # Test for a regression found during MDEV-25506 rewrite of DROP
--echo #
CREATE TEMPORARY TABLE t1 (a INT) ENGINE=InnoDB;
LOCK TABLE t1 READ;
TRUNCATE TABLE t1;
TRUNCATE TABLE t1;
UNLOCK TABLES;
DROP TEMPORARY TABLE t1;
--echo # End of 10.6 tests

View File

@@ -39,10 +39,10 @@ let $wait_condition=
info = 'SELECT * FROM t1 LOCK IN SHARE MODE'; info = 'SELECT * FROM t1 LOCK IN SHARE MODE';
--source include/wait_condition.inc --source include/wait_condition.inc
SET innodb_lock_wait_timeout=1;
--error ER_LOCK_WAIT_TIMEOUT
DROP TABLE t2; DROP TABLE t2;
--source include/restart_mysqld.inc
disconnect con1; disconnect con1;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
@@ -51,8 +51,10 @@ XA ROLLBACK 'x';
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
--error ER_LOCK_WAIT_TIMEOUT
DROP TABLE t2;
XA ROLLBACK 'y';
DROP TABLE t2;
SET GLOBAL innodb_fast_shutdown=0; SET GLOBAL innodb_fast_shutdown=0;
--source include/restart_mysqld.inc --source include/restart_mysqld.inc
XA ROLLBACK 'y';

View File

@@ -33,6 +33,10 @@ connection default;
disconnect ddl1; disconnect ddl1;
disconnect ddl2; disconnect ddl2;
disconnect ddl3; disconnect ddl3;
SET @saved_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET GLOBAL innodb_purge_rseg_truncate_frequency = 1;
InnoDB 0 transactions not purged
SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency;
CHECK TABLE t1,t2,t3; CHECK TABLE t1,t2,t3;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t1 check status OK test.t1 check status OK
@@ -151,6 +155,5 @@ id title body
1 MySQL Tutorial DBMS stands for Database... 1 MySQL Tutorial DBMS stands for Database...
2 MariaDB Tutorial DB means Database ... 2 MariaDB Tutorial DB means Database ...
DROP TABLE mdev19073, mdev19073_2; DROP TABLE mdev19073, mdev19073_2;
SELECT * FROM information_schema.innodb_sys_tables SELECT * FROM information_schema.innodb_sys_tables WHERE name LIKE 'test/%';
WHERE name LIKE 'test/%' AND name NOT LIKE 'test/#sql-ib%';
TABLE_ID NAME FLAG N_COLS SPACE ROW_FORMAT ZIP_PAGE_SIZE SPACE_TYPE TABLE_ID NAME FLAG N_COLS SPACE ROW_FORMAT ZIP_PAGE_SIZE SPACE_TYPE

View File

@@ -20,12 +20,13 @@ DROP TABLE t;
CREATE TABLE t1 (pk INT, a VARCHAR(8), PRIMARY KEY(pk), CREATE TABLE t1 (pk INT, a VARCHAR(8), PRIMARY KEY(pk),
FULLTEXT KEY(a)) ENGINE=InnoDB; FULLTEXT KEY(a)) ENGINE=InnoDB;
CREATE TABLE t2 (b INT, FOREIGN KEY(b) REFERENCES t1(pk)) ENGINE=InnoDB; CREATE TABLE t2 (b INT, FOREIGN KEY(b) REFERENCES t1(pk)) ENGINE=InnoDB;
DROP TABLE t1; DROP TABLE/*foo*/ t1;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails
SET DEBUG_DBUG="+d,fts_instrument_sync"; SET DEBUG_DBUG="+d,fts_instrument_sync";
INSERT INTO t1 VALUES(1, "mariadb"); INSERT INTO t1 VALUES(1, "mariadb");
ALTER TABLE t1 FORCE; ALTER TABLE t1 FORCE;
DROP TABLE t2, t1; DROP TABLE t2, t1;
SET SESSION debug_dbug=@saved_debug_dbug;
# #
# MDEV-25200 Index count mismatch due to aborted FULLTEXT INDEX # MDEV-25200 Index count mismatch due to aborted FULLTEXT INDEX
# #
@@ -56,12 +57,10 @@ DROP TABLE t1;
# MDEV-25663 Double free of transaction during TRUNCATE # MDEV-25663 Double free of transaction during TRUNCATE
# #
call mtr.add_suppression("InnoDB: \\(Too many concurrent transactions\\)"); call mtr.add_suppression("InnoDB: \\(Too many concurrent transactions\\)");
SET DEBUG_DBUG='-d,ib_create_table_fail_too_many_trx';
CREATE TABLE t1 (b CHAR(12), FULLTEXT KEY(b)) engine=InnoDB; CREATE TABLE t1 (b CHAR(12), FULLTEXT KEY(b)) engine=InnoDB;
SET @save_dbug= @@debug_dbug;
SET debug_dbug='+d,ib_create_table_fail_too_many_trx'; SET debug_dbug='+d,ib_create_table_fail_too_many_trx';
TRUNCATE t1; TRUNCATE t1;
ERROR HY000: Got error -1 "Internal error < 0 (Not system error)" from storage engine InnoDB ERROR HY000: Got error -1 "Internal error < 0 (Not system error)" from storage engine InnoDB
SET debug_dbug=@save_dbug; SET debug_dbug=@saved_debug_dbug;
DROP TABLE t1; DROP TABLE t1;
# End of 10.3 tests # End of 10.3 tests

View File

@@ -100,6 +100,14 @@ disconnect ddl1;
disconnect ddl2; disconnect ddl2;
disconnect ddl3; disconnect ddl3;
# Ensure that the history list length will actually be decremented by purge.
SET @saved_frequency = @@GLOBAL.innodb_purge_rseg_truncate_frequency;
SET GLOBAL innodb_purge_rseg_truncate_frequency = 1;
# Wait for purge, so that any #sql-ib.ibd files from the previous kill
# will be deleted.
source ../../innodb/include/wait_all_purged.inc;
SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency;
CHECK TABLE t1,t2,t3; CHECK TABLE t1,t2,t3;
DROP TABLE t1,t2,t3; DROP TABLE t1,t2,t3;
@@ -245,17 +253,4 @@ SELECT * FROM mdev19073_2 WHERE MATCH (title, body)
AGAINST ('Database' IN NATURAL LANGUAGE MODE); AGAINST ('Database' IN NATURAL LANGUAGE MODE);
DROP TABLE mdev19073, mdev19073_2; DROP TABLE mdev19073, mdev19073_2;
if (!$have_debug) SELECT * FROM information_schema.innodb_sys_tables WHERE name LIKE 'test/%';
{
--disable_query_log
# Some errors are reported despite the MDEV-24626 fix.
call mtr.add_suppression("InnoDB: Cannot (read first page of|open datafile for read-only:) '\\./test/(FTS_|#sql-(alter|backup)-).*\\.ibd'");
call mtr.add_suppression("InnoDB: Datafile '\\./test/(FTS_|#sql-(alter|backup)-).*\\.ibd' is corrupted");
call mtr.add_suppression("InnoDB: (The error means|Operating system error)");
call mtr.add_suppression("InnoDB: Ignoring tablespace for test/(FTS_|#sql-(backup|alter)-).* because it could not be opened\\.");
call mtr.add_suppression("InnoDB: Expected tablespace id [1-9][0-9]* but found 0 in the file .*/test/(FTS_|#sql-(alter|backup)-).*\\.ibd");
--enable_query_log
}
SELECT * FROM information_schema.innodb_sys_tables
WHERE name LIKE 'test/%' AND name NOT LIKE 'test/#sql-ib%';

View File

@@ -48,12 +48,13 @@ CREATE TABLE t1 (pk INT, a VARCHAR(8), PRIMARY KEY(pk),
FULLTEXT KEY(a)) ENGINE=InnoDB; FULLTEXT KEY(a)) ENGINE=InnoDB;
CREATE TABLE t2 (b INT, FOREIGN KEY(b) REFERENCES t1(pk)) ENGINE=InnoDB; CREATE TABLE t2 (b INT, FOREIGN KEY(b) REFERENCES t1(pk)) ENGINE=InnoDB;
--error ER_ROW_IS_REFERENCED_2 --error ER_ROW_IS_REFERENCED_2
DROP TABLE t1; DROP TABLE/*foo*/ t1;
SET DEBUG_DBUG="+d,fts_instrument_sync"; SET DEBUG_DBUG="+d,fts_instrument_sync";
INSERT INTO t1 VALUES(1, "mariadb"); INSERT INTO t1 VALUES(1, "mariadb");
ALTER TABLE t1 FORCE; ALTER TABLE t1 FORCE;
# Cleanup # Cleanup
DROP TABLE t2, t1; DROP TABLE t2, t1;
SET SESSION debug_dbug=@saved_debug_dbug;
--echo # --echo #
--echo # MDEV-25200 Index count mismatch due to aborted FULLTEXT INDEX --echo # MDEV-25200 Index count mismatch due to aborted FULLTEXT INDEX
@@ -88,13 +89,11 @@ DROP TABLE t1;
--echo # MDEV-25663 Double free of transaction during TRUNCATE --echo # MDEV-25663 Double free of transaction during TRUNCATE
--echo # --echo #
call mtr.add_suppression("InnoDB: \\(Too many concurrent transactions\\)"); call mtr.add_suppression("InnoDB: \\(Too many concurrent transactions\\)");
SET DEBUG_DBUG='-d,ib_create_table_fail_too_many_trx';
CREATE TABLE t1 (b CHAR(12), FULLTEXT KEY(b)) engine=InnoDB; CREATE TABLE t1 (b CHAR(12), FULLTEXT KEY(b)) engine=InnoDB;
SET @save_dbug= @@debug_dbug;
SET debug_dbug='+d,ib_create_table_fail_too_many_trx'; SET debug_dbug='+d,ib_create_table_fail_too_many_trx';
--error ER_GET_ERRNO --error ER_GET_ERRNO
TRUNCATE t1; TRUNCATE t1;
SET debug_dbug=@save_dbug; SET debug_dbug=@saved_debug_dbug;
DROP TABLE t1; DROP TABLE t1;
--echo # End of 10.3 tests --echo # End of 10.3 tests

View File

@@ -111,6 +111,7 @@ ALTER TABLE t1 IMPORT TABLESPACE;
ERROR HY000: Index for table 't1' is corrupt; try to repair it ERROR HY000: Index for table 't1' is corrupt; try to repair it
SET SESSION debug_dbug=@saved_debug_dbug; SET SESSION debug_dbug=@saved_debug_dbug;
restore: t1 .ibd and .cfg files restore: t1 .ibd and .cfg files
ALTER TABLE t1 IMPORT TABLESPACE;
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 ( CREATE TABLE t1 (
c1 BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, c1 BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,

View File

@@ -162,11 +162,8 @@ SELECT count(*) FROM t7_restart;
--echo # --echo #
--source include/shutdown_mysqld.inc --source include/shutdown_mysqld.inc
--let $regexp=/#sql-ib[0-9a-f]+\.ibd\n//
--echo ---- MYSQL_DATA_DIR/test --echo ---- MYSQL_DATA_DIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
@@ -174,7 +171,6 @@ SELECT count(*) FROM t7_restart;
--list_files $MYSQL_TMP_DIR/alt_dir --list_files $MYSQL_TMP_DIR/alt_dir
--echo ---- MYSQL_TMP_DIR/alt_dir/test --echo ---- MYSQL_TMP_DIR/alt_dir/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
@@ -263,13 +259,11 @@ SHOW CREATE TABLE t7_restart;
--echo ---- MYSQL_DATA_DIR/test --echo ---- MYSQL_DATA_DIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQL_TMP_DIR/alt_dir/test --echo ---- MYSQL_TMP_DIR/alt_dir/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
@@ -320,13 +314,11 @@ RENAME TABLE t5_restart TO t55_restart;
--echo ---- MYSQL_DATA_DIR/test --echo ---- MYSQL_DATA_DIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQL_TMP_DIR/alt_dir/test --echo ---- MYSQL_TMP_DIR/alt_dir/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
@@ -358,13 +350,11 @@ SHOW CREATE TABLE t77_restart;
--echo ---- MYSQL_DATA_DIR/test --echo ---- MYSQL_DATA_DIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQL_TMP_DIR/alt_dir/test --echo ---- MYSQL_TMP_DIR/alt_dir/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
@@ -404,19 +394,16 @@ SHOW CREATE TABLE t77_restart;
--mkdir $MYSQL_TMP_DIR/new_dir/test --mkdir $MYSQL_TMP_DIR/new_dir/test
--echo ---- MYSQL_DATA_DIR/test --echo ---- MYSQL_DATA_DIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQL_TMP_DIR/alt_dir/test --echo ---- MYSQL_TMP_DIR/alt_dir/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQL_TMP_DIR/new_dir/test --echo ---- MYSQL_TMP_DIR/new_dir/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/new_dir/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/new_dir/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
@@ -466,19 +453,16 @@ SHOW CREATE TABLE t77_restart;
--echo ---- MYSQL_DATA_DIR/test --echo ---- MYSQL_DATA_DIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQL_TMP_DIR/alt_dir/test --echo ---- MYSQL_TMP_DIR/alt_dir/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/alt_dir/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQL_TMP_DIR/new_dir/test --echo ---- MYSQL_TMP_DIR/new_dir/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/new_dir/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/new_dir/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
@@ -522,13 +506,11 @@ SHOW CREATE TABLE t77_restart;
--echo ---- MYSQL_DATA_DIR/test --echo ---- MYSQL_DATA_DIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQL_TMP_DIR/new_dir/test --echo ---- MYSQL_TMP_DIR/new_dir/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/new_dir/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/new_dir/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
@@ -570,13 +552,11 @@ SHOW CREATE TABLE t77_restart;
--echo ---- MYSQL_DATA_DIR/test --echo ---- MYSQL_DATA_DIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_DATA_DIR/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQL_TMP_DIR/new_dir/test --echo ---- MYSQL_TMP_DIR/new_dir/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/new_dir/test --list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQL_TMP_DIR/new_dir/test
--replace_regex $regexp
--replace_result #P# #p# #SP# #sp# --replace_result #P# #p# #SP# #sp#
--cat_file $MYSQLD_DATADIR.files.txt --cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt --remove_file $MYSQLD_DATADIR.files.txt

View File

@@ -280,6 +280,7 @@ do "$ENV{MTR_SUITE_DIR}/../innodb/include/innodb-util.pl";
ib_restore_tablespaces("test", "t1"); ib_restore_tablespaces("test", "t1");
EOF EOF
ALTER TABLE t1 IMPORT TABLESPACE;
DROP TABLE t1; DROP TABLE t1;
# #

View File

@@ -6,7 +6,6 @@
--list_files_write_file $DATADIR.files.txt $DATADIR/test --list_files_write_file $DATADIR.files.txt $DATADIR/test
--replace_result #p# #P# #sp# #SP# --replace_result #p# #P# #sp# #SP#
--replace_regex /#sql-ib[1-9][0-9]*\.ibd\n//
--cat_file $DATADIR.files.txt --cat_file $DATADIR.files.txt
--remove_file $DATADIR.files.txt --remove_file $DATADIR.files.txt
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
@@ -20,7 +19,7 @@ SELECT * FROM t1;
--echo # State after crash (before recovery) --echo # State after crash (before recovery)
--list_files_write_file $DATADIR.files.txt $DATADIR/test --list_files_write_file $DATADIR.files.txt $DATADIR/test
--replace_result #p# #P# #sp# #SP# #tmp# #TMP# --replace_result #p# #P# #sp# #SP# #tmp# #TMP#
--replace_regex /sql-exchange.*\./sql-exchange./ /sql-shadow-[0-9a-f]*-/sql-shadow-/ /#sql-ib[1-9][0-9]*\.ibd\n// --replace_regex /sql-exchange.*\./sql-exchange./ /sql-shadow-[0-9a-f]*-/sql-shadow-/
--cat_file $DATADIR.files.txt --cat_file $DATADIR.files.txt
--remove_file $DATADIR.files.txt --remove_file $DATADIR.files.txt
--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
@@ -29,7 +28,6 @@ SELECT * FROM t1;
--echo # State after crash recovery --echo # State after crash recovery
--list_files_write_file $DATADIR.files.txt $DATADIR/test --list_files_write_file $DATADIR.files.txt $DATADIR/test
--replace_result #p# #P# #sp# #SP# --replace_result #p# #P# #sp# #SP#
--replace_regex /#sql-ib[1-9][0-9]*\.ibd\n//
--cat_file $DATADIR.files.txt --cat_file $DATADIR.files.txt
--remove_file $DATADIR.files.txt --remove_file $DATADIR.files.txt
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;

View File

@@ -93,17 +93,9 @@ SHOW WARNINGS;
--echo # Verifying .frm, .par, .isl & .ibd files --echo # Verifying .frm, .par, .isl & .ibd files
--echo ---- MYSQLD_DATADIR/test --echo ---- MYSQLD_DATADIR/test
--let $regexp=/#sql-ib[0-9a-f]+\.ibd\n// --list_files $MYSQLD_DATADIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQLD_DATADIR/test
--replace_regex $regexp
--cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQLTEST_VARDIR/mysql-test-data-dir/test --echo ---- MYSQLTEST_VARDIR/mysql-test-data-dir/test
--let $regexp=/#sql-ib[0-9a-f]+\.ibd\n// --list_files $MYSQLTEST_VARDIR/mysql-test-data-dir/test
--list_files_write_file $MYSQLTEST_VARDIR/files.txt $MYSQLTEST_VARDIR/mysql-test-data-dir/test
--replace_regex $regexp
--cat_file $MYSQLTEST_VARDIR/files.txt
--remove_file $MYSQLTEST_VARDIR/files.txt
--echo # The ibd tablespaces should not be directly under the DATA DIRECTORY --echo # The ibd tablespaces should not be directly under the DATA DIRECTORY
--echo ---- MYSQLTEST_VARDIR/mysql-test-data-dir --echo ---- MYSQLTEST_VARDIR/mysql-test-data-dir
--list_files $MYSQLTEST_VARDIR/mysql-test-data-dir --list_files $MYSQLTEST_VARDIR/mysql-test-data-dir
@@ -122,11 +114,7 @@ ALTER TABLE t1 engine=MyISAM;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
--echo # Verifying .frm, .par and MyISAM files (.MYD, MYI) --echo # Verifying .frm, .par and MyISAM files (.MYD, MYI)
--let $regexp=/#sql-ib[0-9a-f]+\.ibd\n// --list_files $MYSQLD_DATADIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQLD_DATADIR/test
--replace_regex $regexp
--cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQLTEST_VARDIR/mysql-test-data-dir --echo ---- MYSQLTEST_VARDIR/mysql-test-data-dir
--list_files $MYSQLTEST_VARDIR/mysql-test-data-dir --list_files $MYSQLTEST_VARDIR/mysql-test-data-dir
--echo ---- MYSQLTEST_VARDIR/mysql-test-idx-dir --echo ---- MYSQLTEST_VARDIR/mysql-test-idx-dir
@@ -142,21 +130,13 @@ ALTER TABLE t1 engine=InnoDB;
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
--echo # Verifying .frm, .par, .isl and InnoDB .ibd files --echo # Verifying .frm, .par, .isl and InnoDB .ibd files
--echo ---- MYSQLD_DATADIR/test --echo ---- MYSQLD_DATADIR/test
--let $regexp=/#sql-ib[0-9a-f]+\.ibd\n// --list_files $MYSQLD_DATADIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQLD_DATADIR/test
--replace_regex $regexp
--cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt
--echo ---- MYSQLTEST_VARDIR/mysql-test-data-dir --echo ---- MYSQLTEST_VARDIR/mysql-test-data-dir
--list_files $MYSQLTEST_VARDIR/mysql-test-data-dir --list_files $MYSQLTEST_VARDIR/mysql-test-data-dir
--echo ---- MYSQLTEST_VARDIR/mysql-test-idx-dir --echo ---- MYSQLTEST_VARDIR/mysql-test-idx-dir
--list_files $MYSQLTEST_VARDIR/mysql-test-idx-dir --list_files $MYSQLTEST_VARDIR/mysql-test-idx-dir
--echo ---- MYSQLTEST_VARDIR/mysql-test-data-dir/test --echo ---- MYSQLTEST_VARDIR/mysql-test-data-dir/test
--let $regexp=/#sql-ib[0-9a-f]+\.ibd\n// --list_files $MYSQLTEST_VARDIR/mysql-test-data-dir/test
--list_files_write_file $MYSQLTEST_VARDIR/files.txt $MYSQLTEST_VARDIR/mysql-test-data-dir/test
--replace_regex $regexp
--cat_file $MYSQLTEST_VARDIR/files.txt
--remove_file $MYSQLTEST_VARDIR/files.txt
DROP TABLE t1; DROP TABLE t1;

View File

@@ -52,11 +52,7 @@ insert into t1 values (1), (11), (21), (33);
SELECT * FROM t1; SELECT * FROM t1;
SHOW CREATE TABLE t1; SHOW CREATE TABLE t1;
--replace_result #p# #P# #sp# #SP# --replace_result #p# #P# #sp# #SP#
--let $regexp=/#sql-ib[0-9a-f]+\.ibd\n// --list_files $MYSQLD_DATADIR/test
--list_files_write_file $MYSQLD_DATADIR.files.txt $MYSQLD_DATADIR/test
--replace_regex $regexp
--cat_file $MYSQLD_DATADIR.files.txt
--remove_file $MYSQLD_DATADIR.files.txt
SET DEBUG_SYNC='before_open_in_get_all_tables SIGNAL parked WAIT_FOR open'; SET DEBUG_SYNC='before_open_in_get_all_tables SIGNAL parked WAIT_FOR open';
SET DEBUG_SYNC='partition_open_error SIGNAL alter WAIT_FOR finish'; SET DEBUG_SYNC='partition_open_error SIGNAL alter WAIT_FOR finish';

View File

@@ -81,18 +81,6 @@ NUMERIC_BLOCK_SIZE 0
ENUM_VALUE_LIST NULL ENUM_VALUE_LIST NULL
READ_ONLY YES READ_ONLY YES
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME INNODB_BACKGROUND_DROP_LIST_EMPTY
SESSION_VALUE NULL
DEFAULT_VALUE OFF
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BOOLEAN
VARIABLE_COMMENT Wait for the background drop list to become empty
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST OFF,ON
READ_ONLY NO
COMMAND_LINE_ARGUMENT OPTIONAL
VARIABLE_NAME INNODB_BUFFER_POOL_CHUNK_SIZE VARIABLE_NAME INNODB_BUFFER_POOL_CHUNK_SIZE
SESSION_VALUE NULL SESSION_VALUE NULL
DEFAULT_VALUE 134217728 DEFAULT_VALUE 134217728

View File

@@ -493,7 +493,6 @@ int ha_init_errors(void)
SETMSG(HA_ERR_INDEX_COL_TOO_LONG, ER_DEFAULT(ER_INDEX_COLUMN_TOO_LONG)); SETMSG(HA_ERR_INDEX_COL_TOO_LONG, ER_DEFAULT(ER_INDEX_COLUMN_TOO_LONG));
SETMSG(HA_ERR_INDEX_CORRUPT, ER_DEFAULT(ER_INDEX_CORRUPT)); SETMSG(HA_ERR_INDEX_CORRUPT, ER_DEFAULT(ER_INDEX_CORRUPT));
SETMSG(HA_FTS_INVALID_DOCID, "Invalid InnoDB FTS Doc ID"); SETMSG(HA_FTS_INVALID_DOCID, "Invalid InnoDB FTS Doc ID");
SETMSG(HA_ERR_TABLE_IN_FK_CHECK, ER_DEFAULT(ER_TABLE_IN_FK_CHECK));
SETMSG(HA_ERR_DISK_FULL, ER_DEFAULT(ER_DISK_FULL)); SETMSG(HA_ERR_DISK_FULL, ER_DEFAULT(ER_DISK_FULL));
SETMSG(HA_ERR_FTS_TOO_MANY_WORDS_IN_PHRASE, "Too many words in a FTS phrase or proximity search"); SETMSG(HA_ERR_FTS_TOO_MANY_WORDS_IN_PHRASE, "Too many words in a FTS phrase or proximity search");
SETMSG(HA_ERR_FK_DEPTH_EXCEEDED, "Foreign key cascade delete/update exceeds"); SETMSG(HA_ERR_FK_DEPTH_EXCEEDED, "Foreign key cascade delete/update exceeds");
@@ -4259,9 +4258,6 @@ void handler::print_error(int error, myf errflag)
case HA_ERR_UNDO_REC_TOO_BIG: case HA_ERR_UNDO_REC_TOO_BIG:
textno= ER_UNDO_RECORD_TOO_BIG; textno= ER_UNDO_RECORD_TOO_BIG;
break; break;
case HA_ERR_TABLE_IN_FK_CHECK:
textno= ER_TABLE_IN_FK_CHECK;
break;
default: default:
{ {
/* The error was "unknown" to this function. /* The error was "unknown" to this function.

View File

@@ -1853,11 +1853,13 @@ handlerton *ha_default_tmp_handlerton(THD *thd);
*/ */
#define HTON_REQUIRES_CLOSE_AFTER_TRUNCATE (1 << 18) #define HTON_REQUIRES_CLOSE_AFTER_TRUNCATE (1 << 18)
/* Truncate requires that all other handlers are closed */
#define HTON_TRUNCATE_REQUIRES_EXCLUSIVE_USE (1 << 19)
/* /*
Used by mysql_inplace_alter_table() to decide if we should call Used by mysql_inplace_alter_table() to decide if we should call
hton->notify_tabledef_changed() before commit (MyRocks) or after (InnoDB). hton->notify_tabledef_changed() before commit (MyRocks) or after (InnoDB).
*/ */
#define HTON_REQUIRES_NOTIFY_TABLEDEF_CHANGED_AFTER_COMMIT (1 << 19) #define HTON_REQUIRES_NOTIFY_TABLEDEF_CHANGED_AFTER_COMMIT (1 << 20)
class Ha_trx_info; class Ha_trx_info;

View File

@@ -6636,8 +6636,8 @@ ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC
ER_BINLOG_UNSAFE_INSERT_TWO_KEYS ER_BINLOG_UNSAFE_INSERT_TWO_KEYS
eng "INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe" eng "INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe"
ER_TABLE_IN_FK_CHECK ER_UNUSED_28
eng "Table is being used in foreign key check" eng "You should never see it"
ER_UNUSED_1 ER_UNUSED_1
eng "You should never see it" eng "You should never see it"

View File

@@ -2580,10 +2580,15 @@ void Locked_tables_list::mark_table_for_reopen(THD *thd, TABLE *table)
{ {
TABLE_SHARE *share= table->s; TABLE_SHARE *share= table->s;
for (TABLE_LIST *table_list= m_locked_tables; for (TABLE_LIST *table_list= m_locked_tables;
table_list; table_list= table_list->next_global) table_list; table_list= table_list->next_global)
{ {
if (table_list->table->s == share) /*
table_list->table can be NULL in the case of TRUNCATE TABLE where
the table was locked twice and one instance closed in
close_all_tables_for_name().
*/
if (table_list->table && table_list->table->s == share)
table_list->table->internal_set_needs_reopen(true); table_list->table->internal_set_needs_reopen(true);
} }
/* This is needed in the case where lock tables where not used */ /* This is needed in the case where lock tables where not used */

View File

@@ -237,6 +237,21 @@ Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref,
DBUG_RETURN(TRUNCATE_FAILED_SKIP_BINLOG); DBUG_RETURN(TRUNCATE_FAILED_SKIP_BINLOG);
table= table_ref->table; table= table_ref->table;
if ((table->file->ht->flags & HTON_TRUNCATE_REQUIRES_EXCLUSIVE_USE) &&
!is_tmp_table)
{
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
DBUG_RETURN(TRUNCATE_FAILED_SKIP_BINLOG);
/*
Get rid of all TABLE instances belonging to this thread
except one to be used for TRUNCATE
*/
close_all_tables_for_name(thd, table->s,
HA_EXTRA_NOT_USED,
table);
}
error= table->file->ha_truncate(); error= table->file->ha_truncate();
if (!is_tmp_table && !error) if (!is_tmp_table && !error)
@@ -366,8 +381,8 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
} }
} }
*hton_can_recreate= !sequence *hton_can_recreate= (!sequence &&
&& ha_check_storage_engine_flag(hton, HTON_CAN_RECREATE); ha_check_storage_engine_flag(hton, HTON_CAN_RECREATE));
if (versioned) if (versioned)
{ {
@@ -496,10 +511,11 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
if (error == TRUNCATE_OK && thd->locked_tables_mode && if (error == TRUNCATE_OK && thd->locked_tables_mode &&
(table_ref->table->file->ht->flags & (table_ref->table->file->ht->flags &
HTON_REQUIRES_CLOSE_AFTER_TRUNCATE)) (HTON_REQUIRES_CLOSE_AFTER_TRUNCATE |
HTON_TRUNCATE_REQUIRES_EXCLUSIVE_USE)))
{ {
thd->locked_tables_list.mark_table_for_reopen(thd, table_ref->table); thd->locked_tables_list.mark_table_for_reopen(thd, table_ref->table);
if (unlikely(thd->locked_tables_list.reopen_tables(thd, true))) if (unlikely(thd->locked_tables_list.reopen_tables(thd, false)))
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
} }

View File

@@ -48,6 +48,7 @@ SET(INNOBASE_SOURCES
dict/dict0stats.cc dict/dict0stats.cc
dict/dict0stats_bg.cc dict/dict0stats_bg.cc
dict/dict0defrag_bg.cc dict/dict0defrag_bg.cc
dict/drop.cc
eval/eval0eval.cc eval/eval0eval.cc
eval/eval0proc.cc eval/eval0proc.cc
fil/fil0fil.cc fil/fil0fil.cc

View File

@@ -1196,21 +1196,29 @@ void btr_free_if_exists(fil_space_t *space, uint32_t page,
} }
} }
/** Free an index tree in a temporary tablespace. /** Drop a temporary table
@param[in] page_id root page id */ @param table temporary table */
void btr_free(const page_id_t page_id) void btr_drop_temporary_table(const dict_table_t &table)
{ {
mtr_t mtr; ut_ad(table.is_temporary());
mtr.start(); ut_ad(table.space == fil_system.temp_space);
mtr.set_log_mode(MTR_LOG_NO_REDO); mtr_t mtr;
mtr.start();
buf_block_t* block = buf_page_get(page_id, 0, RW_X_LATCH, &mtr); for (const dict_index_t *index= table.indexes.start; index;
index= dict_table_get_next_index(index))
if (block) { {
btr_free_but_not_root(block, MTR_LOG_NO_REDO); if (buf_block_t *block= buf_page_get_low({SRV_TMP_SPACE_ID, index->page}, 0,
btr_free_root(block, &mtr); RW_X_LATCH, nullptr, BUF_GET, &mtr,
} nullptr, false))
mtr.commit(); {
btr_free_but_not_root(block, MTR_LOG_NO_REDO);
mtr.set_log_mode(MTR_LOG_NO_REDO);
btr_free_root(block, &mtr);
mtr.commit();
mtr.start();
}
}
mtr.commit();
} }
/** Read the last used AUTO_INCREMENT value from PAGE_ROOT_AUTO_INC. /** Read the last used AUTO_INCREMENT value from PAGE_ROOT_AUTO_INC.

View File

@@ -1404,22 +1404,19 @@ btr_cur_search_to_nth_level_func(
info->n_searches++; info->n_searches++;
# endif # endif
if (autoinc == 0 if (autoinc == 0
&& latch_mode <= BTR_MODIFY_LEAF
&& info->last_hash_succ
# ifdef MYSQL_INDEX_DISABLE_AHI
&& !index->disable_ahi
# endif
&& !estimate && !estimate
# ifdef PAGE_CUR_LE_OR_EXTENDS && latch_mode <= BTR_MODIFY_LEAF
&& mode != PAGE_CUR_LE_OR_EXTENDS && !modify_external
# endif /* PAGE_CUR_LE_OR_EXTENDS */
&& !dict_index_is_spatial(index)
/* If !ahi_latch, we do a dirty read of /* If !ahi_latch, we do a dirty read of
btr_search_enabled below, and btr_search_guess_on_hash() btr_search_enabled below, and btr_search_guess_on_hash()
will have to check it again. */ will have to check it again. */
&& btr_search_enabled && btr_search_enabled
&& !modify_external # ifdef PAGE_CUR_LE_OR_EXTENDS
&& mode != PAGE_CUR_LE_OR_EXTENDS
# endif /* PAGE_CUR_LE_OR_EXTENDS */
&& info->last_hash_succ
&& !(tuple->info_bits & REC_INFO_MIN_REC_FLAG) && !(tuple->info_bits & REC_INFO_MIN_REC_FLAG)
&& !index->is_spatial() && !index->table->is_temporary()
&& btr_search_guess_on_hash(index, info, tuple, mode, && btr_search_guess_on_hash(index, info, tuple, mode,
latch_mode, cursor, latch_mode, cursor,
ahi_latch, mtr)) { ahi_latch, mtr)) {
@@ -2443,9 +2440,6 @@ need_opposite_intention:
btr_search_build_page_hash_index() before building a btr_search_build_page_hash_index() before building a
page hash index, while holding search latch. */ page hash index, while holding search latch. */
if (!btr_search_enabled) { if (!btr_search_enabled) {
# ifdef MYSQL_INDEX_DISABLE_AHI
} else if (index->disable_ahi) {
# endif
} else if (tuple->info_bits & REC_INFO_MIN_REC_FLAG) { } else if (tuple->info_bits & REC_INFO_MIN_REC_FLAG) {
ut_ad(index->is_instant()); ut_ad(index->is_instant());
/* This may be a search tuple for /* This may be a search tuple for
@@ -2453,6 +2447,8 @@ need_opposite_intention:
ut_ad(tuple->is_metadata() ut_ad(tuple->is_metadata()
|| (tuple->is_metadata(tuple->info_bits || (tuple->is_metadata(tuple->info_bits
^ REC_STATUS_INSTANT))); ^ REC_STATUS_INSTANT)));
} else if (index->is_spatial()) {
} else if (index->table->is_temporary()) {
} else if (rec_is_metadata(btr_cur_get_rec(cursor), *index)) { } else if (rec_is_metadata(btr_cur_get_rec(cursor), *index)) {
/* Only user records belong in the adaptive /* Only user records belong in the adaptive
hash index. */ hash index. */
@@ -3602,13 +3598,11 @@ fail_err:
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
if (!leaf) { if (!leaf) {
# ifdef MYSQL_INDEX_DISABLE_AHI
} else if (index->disable_ahi) {
# endif
} else if (entry->info_bits & REC_INFO_MIN_REC_FLAG) { } else if (entry->info_bits & REC_INFO_MIN_REC_FLAG) {
ut_ad(entry->is_metadata()); ut_ad(entry->is_metadata());
ut_ad(index->is_instant()); ut_ad(index->is_instant());
ut_ad(flags == BTR_NO_LOCKING_FLAG); ut_ad(flags == BTR_NO_LOCKING_FLAG);
} else if (index->table->is_temporary()) {
} else { } else {
srw_lock* ahi_latch = btr_search_sys.get_latch(*index); srw_lock* ahi_latch = btr_search_sys.get_latch(*index);
if (!reorg && cursor->flag == BTR_CUR_HASH) { if (!reorg && cursor->flag == BTR_CUR_HASH) {
@@ -3811,14 +3805,12 @@ btr_cur_pessimistic_insert(
ut_ad(!big_rec_vec); ut_ad(!big_rec_vec);
} else { } else {
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
# ifdef MYSQL_INDEX_DISABLE_AHI
if (index->disable_ahi); else
# endif
if (entry->info_bits & REC_INFO_MIN_REC_FLAG) { if (entry->info_bits & REC_INFO_MIN_REC_FLAG) {
ut_ad(entry->is_metadata()); ut_ad(entry->is_metadata());
ut_ad(index->is_instant()); ut_ad(index->is_instant());
ut_ad(flags & BTR_NO_LOCKING_FLAG); ut_ad(flags & BTR_NO_LOCKING_FLAG);
ut_ad(!(flags & BTR_CREATE_FLAG)); ut_ad(!(flags & BTR_CREATE_FLAG));
} else if (index->table->is_temporary()) {
} else { } else {
btr_search_update_hash_on_insert( btr_search_update_hash_on_insert(
cursor, btr_search_sys.get_latch(*index)); cursor, btr_search_sys.get_latch(*index));

View File

@@ -293,11 +293,7 @@ is NOT protected by any semaphore, to save CPU time! Do not assume its fields
are consistent. are consistent.
@param[in,out] info search info @param[in,out] info search info
@param[in] cursor cursor which was just positioned */ @param[in] cursor cursor which was just positioned */
static static void btr_search_info_update_hash(btr_search_t *info, btr_cur_t *cursor)
void
btr_search_info_update_hash(
btr_search_t* info,
btr_cur_t* cursor)
{ {
dict_index_t* index = cursor->index; dict_index_t* index = cursor->index;
int cmp; int cmp;
@@ -1280,7 +1276,6 @@ retry:
assert_block_ahi_valid(block); assert_block_ahi_valid(block);
if (!index || !btr_search_enabled) { if (!index || !btr_search_enabled) {
if (is_freed) { if (is_freed) {
part->latch.wr_unlock(); part->latch.wr_unlock();
@@ -1290,9 +1285,7 @@ retry:
return; return;
} }
#ifdef MYSQL_INDEX_DISABLE_AHI ut_ad(!index->table->is_temporary());
ut_ad(!index->disable_ahi);
#endif
ut_ad(btr_search_enabled); ut_ad(btr_search_enabled);
ut_ad(block->page.id().space() == index->table->space_id); ut_ad(block->page.id().space() == index->table->space_id);
@@ -1479,9 +1472,8 @@ btr_search_build_page_hash_index(
rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs* offsets = offsets_; rec_offs* offsets = offsets_;
#ifdef MYSQL_INDEX_DISABLE_AHI ut_ad(!index->table->is_temporary());
if (index->disable_ahi) return;
#endif
if (!btr_search_enabled) { if (!btr_search_enabled) {
return; return;
} }
@@ -1661,8 +1653,7 @@ exit_func:
/** Updates the search info. /** Updates the search info.
@param[in,out] info search info @param[in,out] info search info
@param[in,out] cursor cursor which was just positioned */ @param[in,out] cursor cursor which was just positioned */
void void btr_search_info_update_slow(btr_search_t *info, btr_cur_t *cursor)
btr_search_info_update_slow(btr_search_t* info, btr_cur_t* cursor)
{ {
srw_lock* ahi_latch = &btr_search_sys.get_part(*cursor->index) srw_lock* ahi_latch = &btr_search_sys.get_part(*cursor->index)
->latch; ->latch;
@@ -1779,7 +1770,7 @@ drop_exit:
/** Updates the page hash index when a single record is deleted from a page. /** Updates the page hash index when a single record is deleted from a page.
@param[in] cursor cursor which was positioned on the record to delete @param[in] cursor cursor which was positioned on the record to delete
using btr_cur_search_, the record is not yet deleted.*/ using btr_cur_search_, the record is not yet deleted.*/
void btr_search_update_hash_on_delete(btr_cur_t* cursor) void btr_search_update_hash_on_delete(btr_cur_t *cursor)
{ {
buf_block_t* block; buf_block_t* block;
const rec_t* rec; const rec_t* rec;
@@ -1790,9 +1781,6 @@ void btr_search_update_hash_on_delete(btr_cur_t* cursor)
rec_offs_init(offsets_); rec_offs_init(offsets_);
ut_ad(page_is_leaf(btr_cur_get_page(cursor))); ut_ad(page_is_leaf(btr_cur_get_page(cursor)));
#ifdef MYSQL_INDEX_DISABLE_AHI
if (cursor->index->disable_ahi) return;
#endif
if (!btr_search_enabled) { if (!btr_search_enabled) {
return; return;
@@ -1810,6 +1798,8 @@ void btr_search_update_hash_on_delete(btr_cur_t* cursor)
return; return;
} }
ut_ad(!cursor->index->table->is_temporary());
if (index != cursor->index) { if (index != cursor->index) {
btr_search_drop_page_hash_index(block); btr_search_drop_page_hash_index(block);
return; return;
@@ -1864,9 +1854,7 @@ void btr_search_update_hash_node_on_insert(btr_cur_t *cursor,
rec_t* rec; rec_t* rec;
ut_ad(ahi_latch == &btr_search_sys.get_part(*cursor->index)->latch); ut_ad(ahi_latch == &btr_search_sys.get_part(*cursor->index)->latch);
#ifdef MYSQL_INDEX_DISABLE_AHI
if (cursor->index->disable_ahi) return;
#endif
if (!btr_search_enabled) { if (!btr_search_enabled) {
return; return;
} }
@@ -1884,6 +1872,8 @@ void btr_search_update_hash_node_on_insert(btr_cur_t *cursor,
return; return;
} }
ut_ad(!cursor->index->table->is_temporary());
if (index != cursor->index) { if (index != cursor->index) {
ut_ad(index->id == cursor->index->id); ut_ad(index->id == cursor->index->id);
btr_search_drop_page_hash_index(block); btr_search_drop_page_hash_index(block);
@@ -1949,9 +1939,7 @@ void btr_search_update_hash_on_insert(btr_cur_t *cursor,
ut_ad(ahi_latch == &btr_search_sys.get_part(*cursor->index)->latch); ut_ad(ahi_latch == &btr_search_sys.get_part(*cursor->index)->latch);
ut_ad(page_is_leaf(btr_cur_get_page(cursor))); ut_ad(page_is_leaf(btr_cur_get_page(cursor)));
#ifdef MYSQL_INDEX_DISABLE_AHI
if (cursor->index->disable_ahi) return;
#endif
if (!btr_search_enabled) { if (!btr_search_enabled) {
return; return;
} }
@@ -1973,9 +1961,8 @@ void btr_search_update_hash_on_insert(btr_cur_t *cursor,
rec = btr_cur_get_rec(cursor); rec = btr_cur_get_rec(cursor);
#ifdef MYSQL_INDEX_DISABLE_AHI ut_ad(!cursor->index->table->is_temporary());
ut_a(!index->disable_ahi);
#endif
if (index != cursor->index) { if (index != cursor->index) {
ut_ad(index->id == cursor->index->id); ut_ad(index->id == cursor->index->id);
btr_search_drop_page_hash_index(block); btr_search_drop_page_hash_index(block);

View File

@@ -897,17 +897,6 @@ rec_corrupted:
return 0; return 0;
} }
/** @return whether SYS_TABLES.NAME is for a '#sql-ib' table */
bool dict_table_t::is_garbage_name(const void *data, size_t size)
{
constexpr size_t suffix= sizeof TEMP_FILE_PREFIX_INNODB;
if (size <= suffix)
return false;
const char *f= static_cast<const char*>(memchr(data, '/', size - suffix));
return f && !memcmp(f + 1, TEMP_FILE_PREFIX_INNODB,
(sizeof TEMP_FILE_PREFIX_INNODB) - 1);
}
/*********************************************************************//** /*********************************************************************//**
Creates a table create graph. Creates a table create graph.
@return own: table create node */ @return own: table create node */
@@ -1388,34 +1377,15 @@ dberr_t dict_sys_t::create_or_check_sys_tables()
return DB_SUCCESS; return DB_SUCCESS;
trx_t *trx= trx_create(); trx_t *trx= trx_create();
trx->dict_operation = true; trx->dict_operation= true;
row_mysql_lock_data_dictionary(trx); row_mysql_lock_data_dictionary(trx);
DBUG_EXECUTE_IF("create_and_drop_garbage",
ut_ad(DB_SUCCESS == que_eval_sql(
nullptr,
"PROCEDURE CREATE_GARBAGE_TABLE_PROC () IS\n"
"BEGIN\n"
"CREATE TABLE\n"
"\"test/" TEMP_FILE_PREFIX_INNODB "-garbage\""
"(ID CHAR);\n"
"CREATE UNIQUE CLUSTERED INDEX PRIMARY ON "
"\"test/" TEMP_FILE_PREFIX_INNODB
"-garbage\"(ID);\n"
"END;\n", false, trx));
row_drop_table_for_mysql("test/"
TEMP_FILE_PREFIX_INNODB "-garbage",
trx, SQLCOM_DROP_DB, true););
/* NOTE: when designing InnoDB's foreign key support in 2001, Heikki Tuuri /* NOTE: when designing InnoDB's foreign key support in 2001, Heikki Tuuri
made a mistake defined table names and the foreign key id to be of type made a mistake and defined table names and the foreign key id to be of type
'CHAR' (internally, really a VARCHAR). CHAR (internally, really VARCHAR). The type should have been VARBINARY. */
The type should have been VARBINARY. */
/* System tables are always created inside the system tablespace. */
const auto srv_file_per_table_backup= srv_file_per_table; const auto srv_file_per_table_backup= srv_file_per_table;
/* We always want SYSTEM tables to be created inside the system
tablespace. */
srv_file_per_table= 0; srv_file_per_table= 0;
dberr_t error; dberr_t error;
const char *tablename; const char *tablename;

View File

@@ -716,18 +716,18 @@ bool dict_table_t::parse_name(char (&db_name)[NAME_LEN + 1],
const size_t db_len= name.dblen(); const size_t db_len= name.dblen();
ut_ad(db_len <= MAX_DATABASE_NAME_LEN); ut_ad(db_len <= MAX_DATABASE_NAME_LEN);
memcpy(db_buf, name.m_name, db_len); memcpy(db_buf, mdl_name.m_name, db_len);
db_buf[db_len]= 0; db_buf[db_len]= 0;
size_t tbl_len= strlen(name.m_name + db_len + 1); size_t tbl_len= strlen(mdl_name.m_name + db_len + 1);
const bool is_temp= name.is_temporary(); const bool is_temp= mdl_name.is_temporary();
if (is_temp); if (is_temp);
else if (const char *is_part= static_cast<const char*> else if (const char *is_part= static_cast<const char*>
(memchr(name.m_name + db_len + 1, '#', tbl_len))) (memchr(mdl_name.m_name + db_len + 1, '#', tbl_len)))
tbl_len= static_cast<size_t>(is_part - &name.m_name[db_len + 1]); tbl_len= static_cast<size_t>(is_part - &mdl_name.m_name[db_len + 1]);
memcpy(tbl_buf, name.m_name + db_len + 1, tbl_len); memcpy(tbl_buf, mdl_name.m_name + db_len + 1, tbl_len);
tbl_buf[tbl_len]= 0; tbl_buf[tbl_len]= 0;
if (!dict_locked) if (!dict_locked)
@@ -1019,13 +1019,13 @@ void dict_sys_t::create()
/** Acquire a reference to a cached table. */ /** Acquire a reference to a cached table. */
inline void dict_sys_t::acquire(dict_table_t* table) inline void dict_sys_t::acquire(dict_table_t *table)
{ {
ut_ad(dict_sys.find(table)); ut_ad(dict_sys.find(table));
if (table->can_be_evicted) if (table->can_be_evicted)
{ {
UT_LIST_REMOVE(dict_sys.table_LRU, table); UT_LIST_REMOVE(table_LRU, table);
UT_LIST_ADD_FIRST(dict_sys.table_LRU, table); UT_LIST_ADD_FIRST(table_LRU, table);
} }
table->acquire(); table->acquire();
@@ -1490,25 +1490,7 @@ dict_table_t::rename_tablespace(const char *new_name, bool replace) const
ut_ad(!is_temporary()); ut_ad(!is_temporary());
if (!space) if (!space)
{
const char *data_dir= DICT_TF_HAS_DATA_DIR(flags)
? data_dir_path : nullptr;
ut_ad(data_dir || !DICT_TF_HAS_DATA_DIR(flags));
if (char *filepath= fil_make_filepath(data_dir, name, IBD,
data_dir != nullptr))
{
fil_delete_tablespace(space_id, true);
os_file_type_t ftype;
bool exists;
/* Delete any temp file hanging around. */
if (os_file_status(filepath, &exists, &ftype) && exists &&
!os_file_delete_if_exists(innodb_temp_file_key, filepath, nullptr))
ib::info() << "Delete of " << filepath << " failed.";
ut_free(filepath);
}
return DB_SUCCESS; return DB_SUCCESS;
}
const char *old_path= UT_LIST_GET_FIRST(space->chain)->name; const char *old_path= UT_LIST_GET_FIRST(space->chain)->name;
fil_space_t::name_type space_name{new_name, strlen(new_name)}; fil_space_t::name_type space_name{new_name, strlen(new_name)};
@@ -1594,17 +1576,33 @@ dict_table_rename_in_cache(
HASH_DELETE(dict_table_t, name_hash, &dict_sys.table_hash, HASH_DELETE(dict_table_t, name_hash, &dict_sys.table_hash,
ut_fold_string(old_name), table); ut_fold_string(old_name), table);
if (strlen(new_name) > strlen(table->name.m_name)) { const bool keep_mdl_name = dict_table_t::is_temporary_name(new_name)
&& !table->name.is_temporary();
if (keep_mdl_name) {
/* Preserve the original table name for
dict_table_t::parse_name() and dict_acquire_mdl_shared(). */
table->mdl_name.m_name = mem_heap_strdup(table->heap,
table->name.m_name);
}
const size_t new_len = strlen(new_name);
if (new_len > strlen(table->name.m_name)) {
/* We allocate MAX_FULL_NAME_LEN + 1 bytes here to avoid /* We allocate MAX_FULL_NAME_LEN + 1 bytes here to avoid
memory fragmentation, we assume a repeated calls of memory fragmentation, we assume a repeated calls of
ut_realloc() with the same size do not cause fragmentation */ ut_realloc() with the same size do not cause fragmentation */
ut_a(strlen(new_name) <= MAX_FULL_NAME_LEN); ut_a(new_len <= MAX_FULL_NAME_LEN);
table->name.m_name = static_cast<char*>( table->name.m_name = static_cast<char*>(
ut_realloc(table->name.m_name, MAX_FULL_NAME_LEN + 1)); ut_realloc(table->name.m_name, MAX_FULL_NAME_LEN + 1));
} }
strcpy(table->name.m_name, new_name); strcpy(table->name.m_name, new_name);
if (!keep_mdl_name) {
table->mdl_name.m_name = table->name.m_name;
}
/* Add table to hash table of tables */ /* Add table to hash table of tables */
HASH_INSERT(dict_table_t, name_hash, &dict_sys.table_hash, fold, HASH_INSERT(dict_table_t, name_hash, &dict_sys.table_hash, fold,
table); table);
@@ -2065,9 +2063,6 @@ dict_index_add_to_cache(
new_index->trx_id = index->trx_id; new_index->trx_id = index->trx_id;
new_index->set_committed(index->is_committed()); new_index->set_committed(index->is_committed());
new_index->nulls_equal = index->nulls_equal; new_index->nulls_equal = index->nulls_equal;
#ifdef MYSQL_INDEX_DISABLE_AHI
new_index->disable_ahi = index->disable_ahi;
#endif
n_ord = new_index->n_uniq; n_ord = new_index->n_uniq;
/* Flag the ordering columns and also set column max_prefix */ /* Flag the ordering columns and also set column max_prefix */

View File

@@ -853,14 +853,6 @@ static ulint dict_check_sys_tables()
rec_get_nth_field_old(rec, DICT_FLD__SYS_TABLES__NAME, rec_get_nth_field_old(rec, DICT_FLD__SYS_TABLES__NAME,
&len)); &len));
if (len == UNIV_SQL_NULL
|| dict_table_t::is_garbage_name(field, len)) {
/* This table will be dropped by
dict_table_t::drop_garbage().
We do not care if the file exists. */
continue;
}
DBUG_PRINT("dict_check_sys_tables", DBUG_PRINT("dict_check_sys_tables",
("name: %*.s", static_cast<int>(len), field)); ("name: %*.s", static_cast<int>(len), field));
@@ -2451,7 +2443,7 @@ corrupted:
<< " failed, the table has missing" << " failed, the table has missing"
" foreign key indexes. Turn off" " foreign key indexes. Turn off"
" 'foreign_key_checks' and try again."; " 'foreign_key_checks' and try again.";
evict:
dict_sys.remove(table); dict_sys.remove(table);
table = NULL; table = NULL;
} else { } else {
@@ -2468,8 +2460,9 @@ corrupted:
if (!srv_force_recovery if (!srv_force_recovery
|| !index || !index
|| !index->is_primary()) { || !index->is_primary()) {
dict_sys.remove(table); ib::warn() << "Failed to load table " << table->name
table = NULL; << ":" << err;
goto evict;
} else if (index->is_corrupted() } else if (index->is_corrupted()
&& table->is_readable()) { && table->is_readable()) {
/* It is possible we force to load a corrupted /* It is possible we force to load a corrupted

View File

@@ -159,6 +159,7 @@ dict_table_t *dict_table_t::create(const span<const char> &name,
table->flags= static_cast<unsigned>(flags) & ((1U << DICT_TF_BITS) - 1); table->flags= static_cast<unsigned>(flags) & ((1U << DICT_TF_BITS) - 1);
table->flags2= static_cast<unsigned>(flags2) & ((1U << DICT_TF2_BITS) - 1); table->flags2= static_cast<unsigned>(flags2) & ((1U << DICT_TF2_BITS) - 1);
table->name.m_name= mem_strdupl(name.data(), name.size()); table->name.m_name= mem_strdupl(name.data(), name.size());
table->mdl_name.m_name= table->name.m_name;
table->is_system_db= dict_mem_table_is_system(table->name.m_name); table->is_system_db= dict_mem_table_is_system(table->name.m_name);
table->space= space; table->space= space;
table->space_id= space ? space->id : ULINT_UNDEFINED; table->space_id= space ? space->id : ULINT_UNDEFINED;
@@ -221,7 +222,6 @@ dict_mem_table_free(
table->referenced_set.~dict_foreign_set(); table->referenced_set.~dict_foreign_set();
ut_free(table->name.m_name); ut_free(table->name.m_name);
table->name.m_name = NULL;
/* Clean up virtual index info structures that are registered /* Clean up virtual index info structures that are registered
with virtual columns */ with virtual columns */

View File

@@ -529,61 +529,35 @@ free the trx object. If it is not NULL then it will be rolled back
only in the case of error, but not freed. only in the case of error, but not freed.
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
static static
dberr_t dberr_t dict_stats_exec_sql(pars_info_t *pinfo, const char* sql, trx_t *trx)
dict_stats_exec_sql(
pars_info_t* pinfo,
const char* sql,
trx_t* trx)
{ {
dberr_t err; ut_d(dict_sys.assert_locked());
bool trx_started = false;
ut_d(dict_sys.assert_locked()); if (!dict_stats_persistent_storage_check(true))
{
pars_info_free(pinfo);
return DB_STATS_DO_NOT_EXIST;
}
if (!dict_stats_persistent_storage_check(true)) { if (trx)
pars_info_free(pinfo); return que_eval_sql(pinfo, sql, FALSE, trx);
return(DB_STATS_DO_NOT_EXIST);
}
if (trx == NULL) { trx= trx_create();
trx = trx_create(); if (srv_read_only_mode)
trx_started = true; trx_start_internal_read_only(trx);
else
trx_start_internal(trx);
if (srv_read_only_mode) { trx->dict_operation_lock_mode= RW_X_LATCH;
trx_start_internal_read_only(trx); dberr_t err= que_eval_sql(pinfo, sql, FALSE, trx);
} else {
trx_start_internal(trx);
}
}
err = que_eval_sql(pinfo, sql, FALSE, trx); /* pinfo is freed here */ if (err == DB_SUCCESS)
trx->commit();
DBUG_EXECUTE_IF("stats_index_error", else
if (!trx_started) { trx->rollback();
err = DB_STATS_DO_NOT_EXIST; trx->dict_operation_lock_mode= 0;
trx->error_state = DB_STATS_DO_NOT_EXIST; trx->free();
}); return err;
if (!trx_started && err == DB_SUCCESS) {
return(DB_SUCCESS);
}
if (err == DB_SUCCESS) {
trx_commit_for_mysql(trx);
} else {
trx->op_info = "rollback of internal trx on stats tables";
trx->dict_operation_lock_mode = RW_X_LATCH;
trx->rollback();
trx->dict_operation_lock_mode = 0;
trx->op_info = "";
ut_a(trx->error_state == DB_SUCCESS);
}
if (trx_started) {
trx->free();
}
return(err);
} }
/*********************************************************************//** /*********************************************************************//**
@@ -671,6 +645,7 @@ dict_stats_table_clone_create(
t->heap = heap; t->heap = heap;
t->name.m_name = mem_heap_strdup(heap, table->name.m_name); t->name.m_name = mem_heap_strdup(heap, table->name.m_name);
t->mdl_name.m_name = t->name.m_name;
t->corrupted = table->corrupted; t->corrupted = table->corrupted;
@@ -2749,6 +2724,9 @@ dict_stats_save(
table_utf8, sizeof(table_utf8)); table_utf8, sizeof(table_utf8));
const time_t now = time(NULL); const time_t now = time(NULL);
trx_t* trx = trx_create();
trx_start_internal(trx);
trx->dict_operation_lock_mode = RW_X_LATCH;
dict_sys_lock(); dict_sys_lock();
pinfo = pars_info_create(); pinfo = pars_info_create();
@@ -2782,20 +2760,21 @@ dict_stats_save(
":clustered_index_size,\n" ":clustered_index_size,\n"
":sum_of_other_index_sizes\n" ":sum_of_other_index_sizes\n"
");\n" ");\n"
"END;", NULL); "END;", trx);
if (UNIV_UNLIKELY(ret != DB_SUCCESS)) { if (UNIV_UNLIKELY(ret != DB_SUCCESS)) {
ib::error() << "Cannot save table statistics for table " ib::error() << "Cannot save table statistics for table "
<< table->name << ": " << ret; << table->name << ": " << ret;
func_exit: rollback_and_exit:
trx->rollback();
free_and_exit:
trx->dict_operation_lock_mode = 0;
dict_sys_unlock(); dict_sys_unlock();
trx->free();
dict_stats_snapshot_free(table); dict_stats_snapshot_free(table);
return ret; return ret;
} }
trx_t* trx = trx_create();
trx_start_internal(trx);
dict_index_t* index; dict_index_t* index;
index_map_t indexes( index_map_t indexes(
(ut_strcmp_functor()), (ut_strcmp_functor()),
@@ -2864,7 +2843,7 @@ func_exit:
stat_description, trx); stat_description, trx);
if (ret != DB_SUCCESS) { if (ret != DB_SUCCESS) {
goto end; goto rollback_and_exit;
} }
} }
@@ -2874,7 +2853,7 @@ func_exit:
"Number of leaf pages " "Number of leaf pages "
"in the index", trx); "in the index", trx);
if (ret != DB_SUCCESS) { if (ret != DB_SUCCESS) {
goto end; goto rollback_and_exit;
} }
ret = dict_stats_save_index_stat(index, now, "size", ret = dict_stats_save_index_stat(index, now, "size",
@@ -2883,15 +2862,12 @@ func_exit:
"Number of pages " "Number of pages "
"in the index", trx); "in the index", trx);
if (ret != DB_SUCCESS) { if (ret != DB_SUCCESS) {
goto end; goto rollback_and_exit;
} }
} }
trx_commit_for_mysql(trx); trx->commit();
goto free_and_exit;
end:
trx->free();
goto func_exit;
} }
/*********************************************************************//** /*********************************************************************//**
@@ -3657,112 +3633,15 @@ transient:
return(DB_SUCCESS); return(DB_SUCCESS);
} }
/** Remove the information for a particular index's stats from the persistent /** Execute DELETE FROM mysql.innodb_table_stats
storage if it exists and if there is data stored for this index. @param database_name database name
This function creates its own trx and commits it. @param table_name table name
@param trx transaction (nullptr=start and commit a new one)
We must modify system tables in a separate transaction in order to
adhere to the InnoDB design constraint that dict_sys.latch prevents
lock waits on system tables. If we modified system and user tables in
the same transaction, we should exclusively hold dict_sys.latch until
the transaction is committed, and effectively block other transactions
that will attempt to open any InnoDB tables. Because we have no
guarantee that user transactions will be committed fast, we cannot
afford to keep the system tables locked in a user transaction.
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
dberr_t dberr_t dict_stats_delete_from_table_stats(const char *database_name,
dict_stats_drop_index( const char *table_name, trx_t *trx)
/*==================*/
const char* db_and_table,/*!< in: db and table, e.g. 'db/table' */
const char* iname, /*!< in: index name */
char* errstr, /*!< out: error message if != DB_SUCCESS
is returned */
ulint errstr_sz)/*!< in: size of the errstr buffer */
{
char db_utf8[MAX_DB_UTF8_LEN];
char table_utf8[MAX_TABLE_UTF8_LEN];
pars_info_t* pinfo;
dberr_t ret;
dict_sys.assert_not_locked();
/* skip indexes whose table names do not contain a database name
e.g. if we are dropping an index from SYS_TABLES */
if (strchr(db_and_table, '/') == NULL) {
return(DB_SUCCESS);
}
dict_fs2utf8(db_and_table, db_utf8, sizeof(db_utf8),
table_utf8, sizeof(table_utf8));
pinfo = pars_info_create();
pars_info_add_str_literal(pinfo, "database_name", db_utf8);
pars_info_add_str_literal(pinfo, "table_name", table_utf8);
pars_info_add_str_literal(pinfo, "index_name", iname);
dict_sys_lock();
ret = dict_stats_exec_sql(
pinfo,
"PROCEDURE DROP_INDEX_STATS () IS\n"
"BEGIN\n"
"DELETE FROM \"" INDEX_STATS_NAME "\" WHERE\n"
"database_name = :database_name AND\n"
"table_name = :table_name AND\n"
"index_name = :index_name;\n"
"END;\n", NULL);
dict_sys_unlock();
if (ret == DB_STATS_DO_NOT_EXIST) {
ret = DB_SUCCESS;
}
if (ret != DB_SUCCESS) {
snprintf(errstr, errstr_sz,
"Unable to delete statistics for index %s"
" from %s%s: %s. They can be deleted later using"
" DELETE FROM %s WHERE"
" database_name = '%s' AND"
" table_name = '%s' AND"
" index_name = '%s';",
iname,
INDEX_STATS_NAME_PRINT,
(ret == DB_LOCK_WAIT_TIMEOUT
? " because the rows are locked"
: ""),
ut_strerr(ret),
INDEX_STATS_NAME_PRINT,
db_utf8,
table_utf8,
iname);
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB: %s\n", errstr);
}
return(ret);
}
/*********************************************************************//**
Executes
DELETE FROM mysql.innodb_table_stats
WHERE database_name = '...' AND table_name = '...';
Creates its own transaction and commits it.
@return DB_SUCCESS or error code */
UNIV_INLINE
dberr_t
dict_stats_delete_from_table_stats(
/*===============================*/
const char* database_name, /*!< in: database name, e.g. 'db' */
const char* table_name) /*!< in: table name, e.g. 'table' */
{ {
pars_info_t* pinfo; pars_info_t* pinfo;
dberr_t ret;
ut_d(dict_sys.assert_locked()); ut_d(dict_sys.assert_locked());
@@ -3771,33 +3650,25 @@ dict_stats_delete_from_table_stats(
pars_info_add_str_literal(pinfo, "database_name", database_name); pars_info_add_str_literal(pinfo, "database_name", database_name);
pars_info_add_str_literal(pinfo, "table_name", table_name); pars_info_add_str_literal(pinfo, "table_name", table_name);
ret = dict_stats_exec_sql( return dict_stats_exec_sql(
pinfo, pinfo,
"PROCEDURE DELETE_FROM_TABLE_STATS () IS\n" "PROCEDURE DELETE_FROM_TABLE_STATS () IS\n"
"BEGIN\n" "BEGIN\n"
"DELETE FROM \"" TABLE_STATS_NAME "\" WHERE\n" "DELETE FROM \"" TABLE_STATS_NAME "\" WHERE\n"
"database_name = :database_name AND\n" "database_name = :database_name AND\n"
"table_name = :table_name;\n" "table_name = :table_name;\n"
"END;\n", NULL); "END;\n", trx);
return(ret);
} }
/*********************************************************************//** /** Execute DELETE FROM mysql.innodb_index_stats
Executes @param database_name database name
DELETE FROM mysql.innodb_index_stats @param table_name table name
WHERE database_name = '...' AND table_name = '...'; @param trx transaction (nullptr=start and commit a new one)
Creates its own transaction and commits it.
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
UNIV_INLINE dberr_t dict_stats_delete_from_index_stats(const char *database_name,
dberr_t const char *table_name, trx_t *trx)
dict_stats_delete_from_index_stats(
/*===============================*/
const char* database_name, /*!< in: database name, e.g. 'db' */
const char* table_name) /*!< in: table name, e.g. 'table' */
{ {
pars_info_t* pinfo; pars_info_t* pinfo;
dberr_t ret;
ut_d(dict_sys.assert_locked()); ut_d(dict_sys.assert_locked());
@@ -3806,375 +3677,144 @@ dict_stats_delete_from_index_stats(
pars_info_add_str_literal(pinfo, "database_name", database_name); pars_info_add_str_literal(pinfo, "database_name", database_name);
pars_info_add_str_literal(pinfo, "table_name", table_name); pars_info_add_str_literal(pinfo, "table_name", table_name);
ret = dict_stats_exec_sql( return dict_stats_exec_sql(
pinfo, pinfo,
"PROCEDURE DELETE_FROM_INDEX_STATS () IS\n" "PROCEDURE DELETE_FROM_INDEX_STATS () IS\n"
"BEGIN\n" "BEGIN\n"
"DELETE FROM \"" INDEX_STATS_NAME "\" WHERE\n" "DELETE FROM \"" INDEX_STATS_NAME "\" WHERE\n"
"database_name = :database_name AND\n" "database_name = :database_name AND\n"
"table_name = :table_name;\n" "table_name = :table_name;\n"
"END;\n", NULL); "END;\n", trx);
return(ret);
} }
/*********************************************************************//** /** Execute DELETE FROM mysql.innodb_index_stats
Removes the statistics for a table and all of its indexes from the @param database_name database name
persistent statistics storage if it exists and if there is data stored for @param table_name table name
the table. This function creates its own transaction and commits it. @param index_name name of the index
@param trx transaction (nullptr=start and commit a new one)
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
dberr_t dberr_t dict_stats_delete_from_index_stats(const char *database_name,
dict_stats_drop_table( const char *table_name,
/*==================*/ const char *index_name, trx_t *trx)
const char* db_and_table, /*!< in: db and table, e.g. 'db/table' */
char* errstr, /*!< out: error message
if != DB_SUCCESS is returned */
ulint errstr_sz) /*!< in: size of errstr buffer */
{
char db_utf8[MAX_DB_UTF8_LEN];
char table_utf8[MAX_TABLE_UTF8_LEN];
dberr_t ret;
ut_d(dict_sys.assert_locked());
/* skip tables that do not contain a database name
e.g. if we are dropping SYS_TABLES */
if (strchr(db_and_table, '/') == NULL) {
return(DB_SUCCESS);
}
/* skip innodb_table_stats and innodb_index_stats themselves */
if (strcmp(db_and_table, TABLE_STATS_NAME) == 0
|| strcmp(db_and_table, INDEX_STATS_NAME) == 0) {
return(DB_SUCCESS);
}
dict_fs2utf8(db_and_table, db_utf8, sizeof(db_utf8),
table_utf8, sizeof(table_utf8));
ret = dict_stats_delete_from_table_stats(db_utf8, table_utf8);
if (ret == DB_SUCCESS) {
ret = dict_stats_delete_from_index_stats(db_utf8, table_utf8);
}
if (ret == DB_STATS_DO_NOT_EXIST) {
ret = DB_SUCCESS;
}
if (ret != DB_SUCCESS) {
snprintf(errstr, errstr_sz,
"Unable to delete statistics for table %s.%s: %s."
" They can be deleted later using"
" DELETE FROM %s WHERE"
" database_name = '%s' AND"
" table_name = '%s';"
" DELETE FROM %s WHERE"
" database_name = '%s' AND"
" table_name = '%s';",
db_utf8, table_utf8,
ut_strerr(ret),
INDEX_STATS_NAME_PRINT,
db_utf8, table_utf8,
TABLE_STATS_NAME_PRINT,
db_utf8, table_utf8);
}
return(ret);
}
/*********************************************************************//**
Executes
UPDATE mysql.innodb_table_stats SET
database_name = '...', table_name = '...'
WHERE database_name = '...' AND table_name = '...';
Creates its own transaction and commits it.
@return DB_SUCCESS or error code */
UNIV_INLINE
dberr_t
dict_stats_rename_table_in_table_stats(
/*===================================*/
const char* old_dbname_utf8,/*!< in: database name, e.g. 'olddb' */
const char* old_tablename_utf8,/*!< in: table name, e.g. 'oldtable' */
const char* new_dbname_utf8,/*!< in: database name, e.g. 'newdb' */
const char* new_tablename_utf8)/*!< in: table name, e.g. 'newtable' */
{ {
pars_info_t* pinfo; pars_info_t* pinfo;
dberr_t ret;
ut_d(dict_sys.assert_locked()); ut_d(dict_sys.assert_locked());
pinfo = pars_info_create(); pinfo = pars_info_create();
pars_info_add_str_literal(pinfo, "old_dbname_utf8", old_dbname_utf8); pars_info_add_str_literal(pinfo, "database_name", database_name);
pars_info_add_str_literal(pinfo, "old_tablename_utf8", old_tablename_utf8); pars_info_add_str_literal(pinfo, "table_name", table_name);
pars_info_add_str_literal(pinfo, "new_dbname_utf8", new_dbname_utf8); pars_info_add_str_literal(pinfo, "index_name", index_name);
pars_info_add_str_literal(pinfo, "new_tablename_utf8", new_tablename_utf8);
ret = dict_stats_exec_sql( return dict_stats_exec_sql(
pinfo, pinfo,
"PROCEDURE RENAME_TABLE_IN_TABLE_STATS () IS\n" "PROCEDURE DELETE_FROM_INDEX_STATS () IS\n"
"BEGIN\n" "BEGIN\n"
"UPDATE \"" TABLE_STATS_NAME "\" SET\n" "DELETE FROM \"" INDEX_STATS_NAME "\" WHERE\n"
"database_name = :new_dbname_utf8,\n" "database_name = :database_name AND\n"
"table_name = :new_tablename_utf8\n" "table_name = :table_name AND\n"
"WHERE\n" "index_name = :index_name;\n"
"database_name = :old_dbname_utf8 AND\n" "END;\n", trx);
"table_name = :old_tablename_utf8;\n"
"END;\n", NULL);
return(ret);
} }
/*********************************************************************//** /** Rename a table in InnoDB persistent stats storage.
Executes @param old_name old table name
UPDATE mysql.innodb_index_stats SET @param new_name new table name
database_name = '...', table_name = '...' @param trx transaction
WHERE database_name = '...' AND table_name = '...';
Creates its own transaction and commits it.
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
UNIV_INLINE dberr_t dict_stats_rename_table(const char *old_name, const char *new_name,
dberr_t trx_t *trx)
dict_stats_rename_table_in_index_stats(
/*===================================*/
const char* old_dbname_utf8,/*!< in: database name, e.g. 'olddb' */
const char* old_tablename_utf8,/*!< in: table name, e.g. 'oldtable' */
const char* new_dbname_utf8,/*!< in: database name, e.g. 'newdb' */
const char* new_tablename_utf8)/*!< in: table name, e.g. 'newtable' */
{ {
pars_info_t* pinfo; /* skip the statistics tables themselves */
dberr_t ret; if (!strcmp(old_name, TABLE_STATS_NAME) ||
!strcmp(old_name, INDEX_STATS_NAME) ||
!strcmp(new_name, TABLE_STATS_NAME) ||
!strcmp(new_name, INDEX_STATS_NAME))
return DB_SUCCESS;
ut_d(dict_sys.assert_locked()); char old_db[MAX_DB_UTF8_LEN];
char new_db[MAX_DB_UTF8_LEN];
char old_table[MAX_TABLE_UTF8_LEN];
char new_table[MAX_TABLE_UTF8_LEN];
pinfo = pars_info_create(); dict_fs2utf8(old_name, old_db, sizeof old_db, old_table, sizeof old_table);
dict_fs2utf8(new_name, new_db, sizeof new_db, new_table, sizeof new_table);
pars_info_add_str_literal(pinfo, "old_dbname_utf8", old_dbname_utf8); if (dict_table_t::is_temporary_name(old_name) ||
pars_info_add_str_literal(pinfo, "old_tablename_utf8", old_tablename_utf8); dict_table_t::is_temporary_name(new_name))
pars_info_add_str_literal(pinfo, "new_dbname_utf8", new_dbname_utf8); {
pars_info_add_str_literal(pinfo, "new_tablename_utf8", new_tablename_utf8); if (dberr_t e= dict_stats_delete_from_table_stats(old_db, old_table, trx))
return e;
return dict_stats_delete_from_index_stats(old_db, old_table, trx);
}
ret = dict_stats_exec_sql( pars_info_t *pinfo= pars_info_create();
pinfo, pars_info_add_str_literal(pinfo, "old_db", old_db);
"PROCEDURE RENAME_TABLE_IN_INDEX_STATS () IS\n" pars_info_add_str_literal(pinfo, "old_table", old_table);
"BEGIN\n" pars_info_add_str_literal(pinfo, "new_db", new_db);
"UPDATE \"" INDEX_STATS_NAME "\" SET\n" pars_info_add_str_literal(pinfo, "new_table", new_table);
"database_name = :new_dbname_utf8,\n"
"table_name = :new_tablename_utf8\n"
"WHERE\n"
"database_name = :old_dbname_utf8 AND\n"
"table_name = :old_tablename_utf8;\n"
"END;\n", NULL);
return(ret); static const char sql[]=
"PROCEDURE RENAME_TABLE_IN_STATS() IS\n"
"BEGIN\n"
"UPDATE \"" TABLE_STATS_NAME "\" SET\n"
"database_name=:new_db, table_name=:new_table\n"
"WHERE database_name=:old_db AND table_name=:old_table;\n"
"UPDATE \"" INDEX_STATS_NAME "\" SET\n"
"database_name=:new_db, table_name=:new_table\n"
"WHERE database_name=:old_db AND table_name=:old_table;\n"
"END;\n";
return dict_stats_exec_sql(pinfo, sql, trx);
} }
/*********************************************************************//** /** Rename an index in InnoDB persistent statistics.
Renames a table in InnoDB persistent stats storage. @param db database name
This function creates its own transaction and commits it. @param table table name
@param old_name old table name
@param new_name new table name
@param trx transaction
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
dberr_t dberr_t dict_stats_rename_index(const char *db, const char *table,
dict_stats_rename_table( const char *old_name, const char *new_name,
/*====================*/ trx_t *trx)
const char* old_name, /*!< in: old name, e.g. 'db/table' */
const char* new_name, /*!< in: new name, e.g. 'db/table' */
char* errstr, /*!< out: error string if != DB_SUCCESS
is returned */
size_t errstr_sz) /*!< in: errstr size */
{ {
char old_db_utf8[MAX_DB_UTF8_LEN]; if (!dict_stats_persistent_storage_check(true))
char new_db_utf8[MAX_DB_UTF8_LEN]; return DB_STATS_DO_NOT_EXIST;
char old_table_utf8[MAX_TABLE_UTF8_LEN]; pars_info_t *pinfo= pars_info_create();
char new_table_utf8[MAX_TABLE_UTF8_LEN];
dberr_t ret;
/* skip innodb_table_stats and innodb_index_stats themselves */ pars_info_add_str_literal(pinfo, "db", db);
if (strcmp(old_name, TABLE_STATS_NAME) == 0 pars_info_add_str_literal(pinfo, "table", table);
|| strcmp(old_name, INDEX_STATS_NAME) == 0 pars_info_add_str_literal(pinfo, "old", old_name);
|| strcmp(new_name, TABLE_STATS_NAME) == 0 pars_info_add_str_literal(pinfo, "new", new_name);
|| strcmp(new_name, INDEX_STATS_NAME) == 0) {
return(DB_SUCCESS); static const char sql[]=
} "PROCEDURE RENAME_INDEX_IN_STATS() IS\n"
"BEGIN\n"
"UPDATE \"" INDEX_STATS_NAME "\" SET index_name=:new\n"
"WHERE database_name=:db AND table_name=:table AND index_name=:old;\n"
"END;\n";
dict_fs2utf8(old_name, old_db_utf8, sizeof(old_db_utf8), return dict_stats_exec_sql(pinfo, sql, trx);
old_table_utf8, sizeof(old_table_utf8));
dict_fs2utf8(new_name, new_db_utf8, sizeof(new_db_utf8),
new_table_utf8, sizeof(new_table_utf8));
dict_sys_lock();
ulint n_attempts = 0;
do {
n_attempts++;
ret = dict_stats_rename_table_in_table_stats(
old_db_utf8, old_table_utf8,
new_db_utf8, new_table_utf8);
if (ret == DB_DUPLICATE_KEY) {
dict_stats_delete_from_table_stats(
new_db_utf8, new_table_utf8);
}
if (ret == DB_STATS_DO_NOT_EXIST) {
ret = DB_SUCCESS;
}
if (ret != DB_SUCCESS) {
dict_sys_unlock();
std::this_thread::sleep_for(
std::chrono::milliseconds(200));
dict_sys_lock();
}
} while ((ret == DB_DEADLOCK
|| ret == DB_DUPLICATE_KEY
|| ret == DB_LOCK_WAIT_TIMEOUT)
&& n_attempts < 5);
if (ret != DB_SUCCESS) {
snprintf(errstr, errstr_sz,
"Unable to rename statistics from"
" %s.%s to %s.%s in %s: %s."
" They can be renamed later using"
" UPDATE %s SET"
" database_name = '%s',"
" table_name = '%s'"
" WHERE"
" database_name = '%s' AND"
" table_name = '%s';",
old_db_utf8, old_table_utf8,
new_db_utf8, new_table_utf8,
TABLE_STATS_NAME_PRINT,
ut_strerr(ret),
TABLE_STATS_NAME_PRINT,
new_db_utf8, new_table_utf8,
old_db_utf8, old_table_utf8);
dict_sys_unlock();
return(ret);
}
/* else */
n_attempts = 0;
do {
n_attempts++;
ret = dict_stats_rename_table_in_index_stats(
old_db_utf8, old_table_utf8,
new_db_utf8, new_table_utf8);
if (ret == DB_DUPLICATE_KEY) {
dict_stats_delete_from_index_stats(
new_db_utf8, new_table_utf8);
}
if (ret == DB_STATS_DO_NOT_EXIST) {
ret = DB_SUCCESS;
}
if (ret != DB_SUCCESS) {
dict_sys_unlock();
std::this_thread::sleep_for(
std::chrono::milliseconds(200));
dict_sys_lock();
}
} while ((ret == DB_DEADLOCK
|| ret == DB_DUPLICATE_KEY
|| ret == DB_LOCK_WAIT_TIMEOUT)
&& n_attempts < 5);
dict_sys_unlock();
if (ret != DB_SUCCESS) {
snprintf(errstr, errstr_sz,
"Unable to rename statistics from"
" %s.%s to %s.%s in %s: %s."
" They can be renamed later using"
" UPDATE %s SET"
" database_name = '%s',"
" table_name = '%s'"
" WHERE"
" database_name = '%s' AND"
" table_name = '%s';",
old_db_utf8, old_table_utf8,
new_db_utf8, new_table_utf8,
INDEX_STATS_NAME_PRINT,
ut_strerr(ret),
INDEX_STATS_NAME_PRINT,
new_db_utf8, new_table_utf8,
old_db_utf8, old_table_utf8);
}
return(ret);
} }
/*********************************************************************//** /** Delete all persistent statistics for a database.
Renames an index in InnoDB persistent stats storage. @param db database name
This function creates its own transaction and commits it. @param trx transaction
@return DB_SUCCESS or error code. DB_STATS_DO_NOT_EXIST will be returned @return DB_SUCCESS or error code */
if the persistent stats do not exist. */ dberr_t dict_stats_delete(const char *db, trx_t *trx)
dberr_t
dict_stats_rename_index(
/*====================*/
const dict_table_t* table, /*!< in: table whose index
is renamed */
const char* old_index_name, /*!< in: old index name */
const char* new_index_name) /*!< in: new index name */
{ {
dict_sys_lock(); static const char sql[] =
"PROCEDURE DROP_DATABASE_STATS () IS\n"
"BEGIN\n"
"DELETE FROM \"" TABLE_STATS_NAME "\" WHERE database_name=:db;\n"
"DELETE FROM \"" INDEX_STATS_NAME "\" WHERE database_name=:db;\n"
"END;\n";
if (!dict_stats_persistent_storage_check(true)) { pars_info_t *pinfo= pars_info_create();
dict_sys_unlock(); pars_info_add_str_literal(pinfo, "db", db);
return(DB_STATS_DO_NOT_EXIST); return dict_stats_exec_sql(pinfo, sql, trx);
}
char dbname_utf8[MAX_DB_UTF8_LEN];
char tablename_utf8[MAX_TABLE_UTF8_LEN];
dict_fs2utf8(table->name.m_name, dbname_utf8, sizeof(dbname_utf8),
tablename_utf8, sizeof(tablename_utf8));
pars_info_t* pinfo;
pinfo = pars_info_create();
pars_info_add_str_literal(pinfo, "dbname_utf8", dbname_utf8);
pars_info_add_str_literal(pinfo, "tablename_utf8", tablename_utf8);
pars_info_add_str_literal(pinfo, "new_index_name", new_index_name);
pars_info_add_str_literal(pinfo, "old_index_name", old_index_name);
dberr_t ret;
ret = dict_stats_exec_sql(
pinfo,
"PROCEDURE RENAME_INDEX_IN_INDEX_STATS () IS\n"
"BEGIN\n"
"UPDATE \"" INDEX_STATS_NAME "\" SET\n"
"index_name = :new_index_name\n"
"WHERE\n"
"database_name = :dbname_utf8 AND\n"
"table_name = :tablename_utf8 AND\n"
"index_name = :old_index_name;\n"
"END;\n", NULL);
dict_sys_unlock();
return(ret);
} }
/* tests @{ */ /* tests @{ */

View File

@@ -0,0 +1,256 @@
/*****************************************************************************
Copyright (c) 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
*****************************************************************************/
/**
@file dict/drop.cc
Data Dictionary Language operations that delete .ibd files */
/* We implement atomic data dictionary operations as follows.
1. A data dictionary transaction is started.
2. We acquire exclusive lock on all the tables that are to be dropped
during the execution of the transaction.
3. We lock the data dictionary cache.
4. All metadata tables will be updated within the single DDL transaction,
including deleting or renaming InnoDB persistent statistics.
4b. If any lock wait would occur while we are holding the dict_sys latches,
we will instantly report a timeout error and roll back the transaction.
5. The transaction metadata is marked as committed.
6. If any files were deleted, we will durably write FILE_DELETE
to the redo log and start deleting the files.
6b. Also purge after a commit may perform file deletion. This is also the
recovery mechanism if the server was killed between step 5 and 6.
7. We unlock the data dictionary cache.
8. The file handles of the unlinked files will be closed. This will actually
reclaim the space in the file system (delete-on-close semantics).
Notes:
(a) Purge will be locked out by MDL. For internal tables related to
FULLTEXT INDEX, purge will not acquire MDL on the user table name,
and therefore, when we are dropping any FTS_ tables, we must suspend
and resume purge to prevent a race condition.
(b) If a transaction needs to both drop and create a table by some
name, it must rename the table in between. This is used by
ha_innobase::truncate() and fts_drop_common_tables().
(c) No data is ever destroyed before the transaction is committed,
so we can trivially roll back the transaction at any time.
Lock waits during a DDL operation are no longer a fatal error
that would cause the InnoDB to hang or to intentionally crash.
(Only ALTER TABLE...DISCARD TABLESPACE may discard data before commit.)
(d) The only changes to the data dictionary cache that are performed
before transaction commit and must be rolled back explicitly are as follows:
(d1) fts_optimize_add_table() to undo fts_optimize_remove_table()
(d2) stats_bg_flag= BG_STAT_NONE to undo dict_stats_stop_bg()
*/
#include "trx0purge.h"
#include "dict0dict.h"
#include "dict0stats.h"
#include "dict0stats_bg.h"
#include "dict0defrag_bg.h"
#include "btr0defragment.h"
#include "que0que.h"
#include "pars0pars.h"
/** Try to drop the foreign key constraints for a persistent table.
@param name name of persistent table
@return error code */
dberr_t trx_t::drop_table_foreign(const table_name_t &name)
{
ut_d(dict_sys.assert_locked());
ut_ad(state == TRX_STATE_ACTIVE);
ut_ad(dict_operation);
ut_ad(dict_operation_lock_mode == RW_X_LATCH);
if (!dict_sys.sys_foreign || !dict_sys.sys_foreign_cols)
return DB_SUCCESS;
pars_info_t *info= pars_info_create();
pars_info_add_str_literal(info, "name", name.m_name);
return que_eval_sql(info,
"PROCEDURE DROP_FOREIGN() IS\n"
"fid CHAR;\n"
"DECLARE CURSOR fk IS\n"
"SELECT ID FROM SYS_FOREIGN\n"
"WHERE FOR_NAME=:name\n"
"AND TO_BINARY(FOR_NAME)=TO_BINARY(:name)\n"
"FOR UPDATE;\n"
"BEGIN\n"
"OPEN fk;\n"
"WHILE 1=1 LOOP\n"
" FETCH fk INTO fid;\n"
" IF (SQL % NOTFOUND)THEN RETURN;END IF;\n"
" DELETE FROM SYS_FOREIGN_COLS"
" WHERE ID=fid;\n"
" DELETE FROM SYS_FOREIGN WHERE ID=fid;\n"
"END LOOP;\n"
"CLOSE fk;\n"
"END;\n", FALSE, this);
}
/** Try to drop the statistics for a persistent table.
@param name name of persistent table
@return error code */
dberr_t trx_t::drop_table_statistics(const table_name_t &name)
{
ut_d(dict_sys.assert_locked());
ut_ad(dict_operation_lock_mode == RW_X_LATCH);
if (strstr(name.m_name, "/" TEMP_FILE_PREFIX_INNODB) ||
!strcmp(name.m_name, TABLE_STATS_NAME) ||
!strcmp(name.m_name, INDEX_STATS_NAME))
return DB_SUCCESS;
char db[MAX_DB_UTF8_LEN], table[MAX_TABLE_UTF8_LEN];
dict_fs2utf8(name.m_name, db, sizeof db, table, sizeof table);
dberr_t err= dict_stats_delete_from_table_stats(db, table, this);
if (err == DB_SUCCESS || err == DB_STATS_DO_NOT_EXIST)
{
err= dict_stats_delete_from_index_stats(db, table, this);
if (err == DB_STATS_DO_NOT_EXIST)
err= DB_SUCCESS;
}
return err;
}
/** Try to drop a persistent table.
@param table persistent table
@param fk whether to drop FOREIGN KEY metadata
@return error code */
dberr_t trx_t::drop_table(const dict_table_t &table)
{
ut_d(dict_sys.assert_locked());
ut_ad(state == TRX_STATE_ACTIVE);
ut_ad(dict_operation);
ut_ad(dict_operation_lock_mode == RW_X_LATCH);
ut_ad(!table.is_temporary());
ut_ad(!(table.stats_bg_flag & BG_STAT_IN_PROGRESS));
/* The table must be exclusively locked by this transaction. */
ut_ad(table.get_ref_count() <= 1);
ut_ad(!table.n_waiting_or_granted_auto_inc_locks);
ut_ad(table.n_lock_x_or_s == 1);
ut_ad(UT_LIST_GET_LEN(table.locks) >= 1);
#ifdef UNIV_DEBUG
bool found_x;
for (lock_t *lock= UT_LIST_GET_FIRST(table.locks); lock;
lock= UT_LIST_GET_NEXT(un_member.tab_lock.locks, lock))
{
ut_ad(lock->trx == this);
if (lock->type_mode == (LOCK_X | LOCK_TABLE))
found_x= true;
else
ut_ad(lock->type_mode == (LOCK_IX | LOCK_TABLE));
}
ut_ad(found_x);
#endif
if (dict_sys.sys_virtual)
{
pars_info_t *info= pars_info_create();
pars_info_add_ull_literal(info, "id", table.id);
if (dberr_t err= que_eval_sql(info,
"PROCEDURE DROP_VIRTUAL() IS\n"
"BEGIN\n"
"DELETE FROM SYS_VIRTUAL"
" WHERE TABLE_ID=:id;\n"
"END;\n", FALSE, this))
return err;
}
/* Once DELETE FROM SYS_INDEXES is committed, purge may invoke
dict_drop_index_tree(). */
if (!(table.flags2 & (DICT_TF2_FTS_HAS_DOC_ID | DICT_TF2_FTS)));
else if (dberr_t err= fts_drop_tables(this, table))
{
ib::error() << "Unable to remove FTS tables for "
<< table.name << ": " << err;
return err;
}
mod_tables.emplace(const_cast<dict_table_t*>(&table), undo_no).
first->second.set_dropped();
pars_info_t *info= pars_info_create();
pars_info_add_ull_literal(info, "id", table.id);
return que_eval_sql(info,
"PROCEDURE DROP_TABLE() IS\n"
"iid CHAR;\n"
"DECLARE CURSOR idx IS\n"
"SELECT ID FROM SYS_INDEXES\n"
"WHERE TABLE_ID=:id FOR UPDATE;\n"
"BEGIN\n"
"OPEN idx;\n"
"WHILE 1 = 1 LOOP\n"
" FETCH idx INTO iid;\n"
" IF (SQL % NOTFOUND) THEN EXIT; END IF;\n"
" DELETE FROM SYS_FIELDS WHERE INDEX_ID=iid;\n"
" DELETE FROM SYS_INDEXES WHERE CURRENT OF idx;\n"
"END LOOP;\n"
"CLOSE idx;\n"
"DELETE FROM SYS_COLUMNS WHERE TABLE_ID=:id;\n"
"DELETE FROM SYS_TABLES WHERE ID=:id;\n"
"END;\n", FALSE, this);
}
/** Commit the transaction, possibly after drop_table().
@param deleted handles of data files that were deleted */
void trx_t::commit(std::vector<pfs_os_file_t> &deleted)
{
ut_ad(dict_operation);
commit_persist();
if (dict_operation)
{
dict_sys.assert_locked();
for (const auto &p : mod_tables)
{
if (p.second.is_dropped())
{
dict_table_t *table= p.first;
dict_stats_recalc_pool_del(table);
dict_stats_defrag_pool_del(table, nullptr);
if (btr_defragment_active)
btr_defragment_remove_table(table);
const fil_space_t *space= table->space;
ut_ad(!strstr(table->name.m_name, "/FTS_") ||
purge_sys.must_wait_FTS());
dict_sys.remove(table);
if (const auto id= space ? space->id : 0)
{
pfs_os_file_t d= fil_delete_tablespace(id);
if (d != OS_FILE_CLOSED)
deleted.emplace_back(d);
}
}
}
}
commit_cleanup();
}

View File

@@ -2312,9 +2312,6 @@ void fil_space_crypt_close_tablespace(const fil_space_t *space)
while (crypt_data->rotate_state.active_threads while (crypt_data->rotate_state.active_threads
|| crypt_data->rotate_state.flushing) { || crypt_data->rotate_state.flushing) {
mysql_mutex_unlock(&crypt_data->mutex); mysql_mutex_unlock(&crypt_data->mutex);
/* release dict mutex so that scrub threads can release their
* table references */
dict_sys.mutex_unlock();
/* wakeup throttle (all) sleepers */ /* wakeup throttle (all) sleepers */
mysql_mutex_lock(&fil_crypt_threads_mutex); mysql_mutex_lock(&fil_crypt_threads_mutex);
@@ -2323,8 +2320,6 @@ void fil_space_crypt_close_tablespace(const fil_space_t *space)
mysql_mutex_unlock(&fil_crypt_threads_mutex); mysql_mutex_unlock(&fil_crypt_threads_mutex);
std::this_thread::sleep_for(std::chrono::milliseconds(20)); std::this_thread::sleep_for(std::chrono::milliseconds(20));
dict_sys.mutex_lock();
mysql_mutex_lock(&crypt_data->mutex);
time_t now = time(0); time_t now = time(0);
@@ -2339,6 +2334,8 @@ void fil_space_crypt_close_tablespace(const fil_space_t *space)
<< crypt_data->rotate_state.flushing << "."; << crypt_data->rotate_state.flushing << ".";
last = now; last = now;
} }
mysql_mutex_lock(&crypt_data->mutex);
} }
mysql_mutex_unlock(&crypt_data->mutex); mysql_mutex_unlock(&crypt_data->mutex);

View File

@@ -1566,39 +1566,61 @@ fil_name_write(
fil_space_t *fil_space_t::check_pending_operations(ulint id) fil_space_t *fil_space_t::check_pending_operations(ulint id)
{ {
ut_a(!is_system_tablespace(id)); ut_a(!is_system_tablespace(id));
bool being_deleted= false;
mysql_mutex_lock(&fil_system.mutex); mysql_mutex_lock(&fil_system.mutex);
fil_space_t *space= fil_space_get_by_id(id); fil_space_t *space= fil_space_get_by_id(id);
if (!space); if (!space);
else if (space->pending() & STOPPING) else if (space->pending() & STOPPING)
space= nullptr; being_deleted= true;
else else
{ {
space->reacquire();
if (space->crypt_data) if (space->crypt_data)
{ {
space->reacquire();
mysql_mutex_unlock(&fil_system.mutex); mysql_mutex_unlock(&fil_system.mutex);
fil_space_crypt_close_tablespace(space); fil_space_crypt_close_tablespace(space);
mysql_mutex_lock(&fil_system.mutex); mysql_mutex_lock(&fil_system.mutex);
space->release();
} }
space->set_stopping(true); being_deleted= space->set_stopping();
space->release();
} }
mysql_mutex_unlock(&fil_system.mutex); mysql_mutex_unlock(&fil_system.mutex);
if (!space) if (!space)
return nullptr; return nullptr;
if (being_deleted)
{
/* A thread executing DDL and another thread executing purge may
be executing fil_delete_tablespace() concurrently for the same
tablespace. Wait for the other thread to complete the operation. */
for (ulint count= 0;; count++)
{
mysql_mutex_lock(&fil_system.mutex);
space= fil_space_get_by_id(id);
ut_ad(!space || space->is_stopping());
mysql_mutex_unlock(&fil_system.mutex);
if (!space)
return nullptr;
/* Issue a warning every 10.24 seconds, starting after 2.56 seconds */
if ((count & 511) == 128)
sql_print_warning("InnoDB: Waiting for tablespace " ULINTPF
" to be deleted", id);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
}
for (ulint count= 0;; count++) for (ulint count= 0;; count++)
{ {
auto pending= space->referenced(); const unsigned pending= space->referenced();
if (!pending) if (!pending)
return space; return space;
/* Give a warning every 10 second, starting after 1 second */ /* Issue a warning every 10.24 seconds, starting after 2.56 seconds */
if ((count % 500) == 50) if ((count & 511) == 128)
ib::warn() << "Trying to delete tablespace '" sql_print_warning("InnoDB: Trying to delete tablespace '%s' "
<< space->chain.start->name << "' but there are " "but there are %u pending operations",
<< pending << " pending operations on it."; space->chain.start->name, id);
std::this_thread::sleep_for(std::chrono::milliseconds(20)); std::this_thread::sleep_for(std::chrono::milliseconds(20));
} }
} }
@@ -1645,117 +1667,60 @@ void fil_close_tablespace(ulint id)
} }
/** Delete a tablespace and associated .ibd file. /** Delete a tablespace and associated .ibd file.
@param[in] id tablespace identifier @param id tablespace identifier
@param[in] if_exists whether to ignore missing tablespace @return detached file handle (to be closed by the caller)
@param[out] detached deatched file handle (if closing is not wanted) @return OS_FILE_CLOSED if no file existed */
@return DB_SUCCESS or error */ pfs_os_file_t fil_delete_tablespace(ulint id)
dberr_t fil_delete_tablespace(ulint id, bool if_exists,
pfs_os_file_t *detached)
{ {
ut_ad(!is_system_tablespace(id)); ut_ad(!is_system_tablespace(id));
ut_ad(!detached || *detached == OS_FILE_CLOSED); pfs_os_file_t handle= OS_FILE_CLOSED;
if (fil_space_t *space= fil_space_t::check_pending_operations(id))
{
/* Before deleting the file(s), persistently write a log record. */
mtr_t mtr;
mtr.start();
mtr.log_file_op(FILE_DELETE, id, space->chain.start->name);
mtr.commit();
log_write_up_to(mtr.commit_lsn(), true);
dberr_t err; /* Remove any additional files. */
fil_space_t *space = fil_space_t::check_pending_operations(id); if (char *cfg_name= fil_make_filepath(space->chain.start->name,
fil_space_t::name_type{}, CFG,
false))
{
os_file_delete_if_exists(innodb_data_file_key, cfg_name, nullptr);
ut_free(cfg_name);
}
if (FSP_FLAGS_HAS_DATA_DIR(space->flags))
RemoteDatafile::delete_link_file(space->name());
if (!space) { /* Remove the directory entry. The file will actually be deleted
err = DB_TABLESPACE_NOT_FOUND; when our caller closes the handle. */
if (!if_exists) { os_file_delete(innodb_data_file_key, space->chain.start->name);
ib::error() << "Cannot delete tablespace " << id
<< " because it is not found"
" in the tablespace memory cache.";
}
func_exit:
ibuf_delete_for_discarded_space(id);
return err;
}
/* IMPORTANT: Because we have set space::stop_new_ops there mysql_mutex_lock(&fil_system.mutex);
can't be any new reads or flushes. We are here /* Sanity checks after reacquiring fil_system.mutex */
because node::n_pending was zero above. However, it is still ut_ad(space == fil_space_get_by_id(id));
possible to have pending read and write requests: ut_ad(!space->referenced());
ut_ad(space->is_stopping());
ut_ad(UT_LIST_GET_LEN(space->chain) == 1);
/* Detach the file handle. */
handle= fil_system.detach(space, true);
mysql_mutex_unlock(&fil_system.mutex);
A read request can happen because the reader thread has mysql_mutex_lock(&log_sys.mutex);
gone through the ::stop_new_ops check in buf_page_init_for_read() if (space->max_lsn)
before the flag was set and has not yet incremented ::n_pending {
when we checked it above. ut_d(space->max_lsn = 0);
fil_system.named_spaces.remove(*space);
}
mysql_mutex_unlock(&log_sys.mutex);
A write request can be issued any time because we don't check fil_space_free_low(space);
fil_space_t::is_stopping() when queueing a block for write. }
We deal with pending write requests in the following function ibuf_delete_for_discarded_space(id);
where we'd minimally evict all dirty pages belonging to this return handle;
space from the flush_list. Note that if a block is IO-fixed
we'll wait for IO to complete.
To deal with potential read requests, we will check the
is_stopping() in fil_space_t::io(). */
err = DB_SUCCESS;
buf_flush_remove_pages(id);
/* If it is a delete then also delete any generated files, otherwise
when we drop the database the remove directory will fail. */
{
/* Before deleting the file, write a log record about
it, so that InnoDB crash recovery will expect the file
to be gone. */
mtr_t mtr;
mtr.start();
mtr.log_file_op(FILE_DELETE, id, space->chain.start->name);
mtr.commit();
/* Even if we got killed shortly after deleting the
tablespace file, the record must have already been
written to the redo log. */
log_write_up_to(mtr.commit_lsn(), true);
if (char* cfg_name = fil_make_filepath(
space->chain.start->name,
fil_space_t::name_type{}, CFG, false)) {
os_file_delete_if_exists(innodb_data_file_key,
cfg_name, nullptr);
ut_free(cfg_name);
}
}
/* Delete the link file pointing to the ibd file we are deleting. */
if (FSP_FLAGS_HAS_DATA_DIR(space->flags)) {
RemoteDatafile::delete_link_file(space->name());
}
mysql_mutex_lock(&fil_system.mutex);
/* Double check the sanity of pending ops after reacquiring
the fil_system::mutex. */
ut_a(space == fil_space_get_by_id(id));
ut_a(!space->referenced());
ut_a(UT_LIST_GET_LEN(space->chain) == 1);
pfs_os_file_t handle = fil_system.detach(space, detached != nullptr);
if (detached) {
*detached = handle;
}
mysql_mutex_unlock(&fil_system.mutex);
mysql_mutex_lock(&log_sys.mutex);
if (space->max_lsn != 0) {
ut_d(space->max_lsn = 0);
fil_system.named_spaces.remove(*space);
}
mysql_mutex_unlock(&log_sys.mutex);
if (!os_file_delete(innodb_data_file_key, space->chain.start->name)
&& !os_file_delete_if_exists(innodb_data_file_key,
space->chain.start->name, NULL)) {
/* Note: This is because we have removed the
tablespace instance from the cache. */
err = DB_IO_ERROR;
}
fil_space_free_low(space);
goto func_exit;
} }
/*******************************************************************//** /*******************************************************************//**

View File

@@ -267,20 +267,20 @@ Datafile::read_first_page(bool read_only_mode)
IORequestReadPartial, m_handle, m_first_page, 0, IORequestReadPartial, m_handle, m_first_page, 0,
page_size, &n_read); page_size, &n_read);
if (err == DB_IO_ERROR && n_read >= UNIV_PAGE_SIZE_MIN) { if (err == DB_SUCCESS) {
page_size >>= 1;
} else if (err == DB_SUCCESS) {
ut_a(n_read == page_size); ut_a(n_read == page_size);
break; break;
}
if (err == DB_IO_ERROR && n_read == 0) {
break;
}
if (err == DB_IO_ERROR && n_read >= UNIV_PAGE_SIZE_MIN) {
page_size >>= 1;
} else if (srv_operation == SRV_OPERATION_BACKUP) { } else if (srv_operation == SRV_OPERATION_BACKUP) {
break; break;
} else { } else {
ib::error() << "Cannot read first page of '" ib::info() << "Cannot read first page of '"
<< m_filepath << "': " << err; << m_filepath << "': " << err;
break; break;
} }

View File

@@ -23,6 +23,9 @@ Full Text Search interface
***********************************************************************/ ***********************************************************************/
#include "trx0roll.h" #include "trx0roll.h"
#ifdef UNIV_DEBUG
# include "trx0purge.h"
#endif
#include "row0mysql.h" #include "row0mysql.h"
#include "row0upd.h" #include "row0upd.h"
#include "dict0types.h" #include "dict0types.h"
@@ -871,7 +874,7 @@ fts_drop_index(
mysql_mutex_unlock(&cache->init_lock); mysql_mutex_unlock(&cache->init_lock);
} }
err = fts_drop_index_tables(trx, index); err = fts_drop_index_tables(trx, *index);
ib_vector_remove(indexes, (const void*) index); ib_vector_remove(indexes, (const void*) index);
@@ -1400,46 +1403,43 @@ fts_cache_add_doc(
} }
} }
/****************************************************************//** /** Drop a table.
Drops a table. If the table can't be found we return a SUCCESS code. @param trx transaction
@return DB_SUCCESS or error code */ @param table_name FTS_ table name
static MY_ATTRIBUTE((nonnull, warn_unused_result)) @param rename whether to rename before dropping
dberr_t @return error code
fts_drop_table( @retval DB_SUCCESS if the table was dropped
/*===========*/ @retval DB_FAIL if the table did not exist */
trx_t* trx, /*!< in: transaction */ static dberr_t fts_drop_table(trx_t *trx, const char *table_name, bool rename)
const char* table_name) /*!< in: table to drop */
{ {
dict_table_t* table; if (dict_table_t *table= dict_table_open_on_name(table_name, TRUE, FALSE,
dberr_t error = DB_SUCCESS; DICT_ERR_IGNORE_DROP))
{
table->release();
if (rename)
{
mem_heap_t *heap= mem_heap_create(FN_REFLEN);
char *tmp= dict_mem_create_temporary_tablename(heap, table->name.m_name,
table->id);
dberr_t err= row_rename_table_for_mysql(table->name.m_name, tmp, trx,
false);
mem_heap_free(heap);
if (err != DB_SUCCESS)
{
ib::error() << "Unable to rename table " << table_name << ": " << err;
return err;
}
}
if (dberr_t err= trx->drop_table(*table))
{
ib::error() << "Unable to drop table " << table->name << ": " << err;
return err;
}
/* Check that the table exists in our data dictionary. return DB_SUCCESS;
Similar to regular drop table case, we will open table with }
DICT_ERR_IGNORE_INDEX_ROOT and DICT_ERR_IGNORE_CORRUPT option */
table = dict_table_open_on_name(
table_name, TRUE, FALSE,
static_cast<dict_err_ignore_t>(
DICT_ERR_IGNORE_INDEX_ROOT | DICT_ERR_IGNORE_CORRUPT));
if (table != 0) { return DB_FAIL;
dict_table_close(table, TRUE, FALSE);
/* Pass nonatomic=false (don't allow data dict unlock),
because the transaction may hold locks on SYS_* tables from
previous calls to fts_drop_table(). */
error = row_drop_table_for_mysql(table_name, trx,
SQLCOM_DROP_DB, false, false);
if (UNIV_UNLIKELY(error != DB_SUCCESS)) {
ib::error() << "Unable to drop FTS index aux table "
<< table_name << ": " << error;
}
} else {
error = DB_FAIL;
}
return(error);
} }
/****************************************************************//** /****************************************************************//**
@@ -1473,7 +1473,7 @@ fts_rename_one_aux_table(
fts_table_new_name[table_new_name_len] = 0; fts_table_new_name[table_new_name_len] = 0;
return row_rename_table_for_mysql( return row_rename_table_for_mysql(
fts_table_old_name, fts_table_new_name, trx, false, false); fts_table_old_name, fts_table_new_name, trx, false);
} }
/****************************************************************//** /****************************************************************//**
@@ -1539,25 +1539,115 @@ fts_rename_aux_tables(
return(DB_SUCCESS); return(DB_SUCCESS);
} }
/** Lock an internal FTS_ table, before fts_drop_table() */
static dberr_t fts_lock_table(trx_t *trx, const char *table_name)
{
ut_ad(purge_sys.must_wait_FTS());
if (dict_table_t *table= dict_table_open_on_name(table_name, false, false,
DICT_ERR_IGNORE_DROP))
{
dberr_t err= lock_table_for_trx(table, trx, LOCK_X);
/* Wait for purge threads to stop using the table. */
for (uint n= 15; table->get_ref_count() > 1; )
{
if (!--n)
{
err= DB_LOCK_WAIT_TIMEOUT;
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
table->release();
return err;
}
return DB_SUCCESS;
}
/** Lock the internal FTS_ tables for an index, before fts_drop_index_tables().
@param trx transaction
@param index fulltext index */
dberr_t fts_lock_index_tables(trx_t *trx, const dict_index_t &index)
{
ut_ad(index.type & DICT_FTS);
fts_table_t fts_table;
char table_name[MAX_FULL_NAME_LEN];
FTS_INIT_INDEX_TABLE(&fts_table, nullptr, FTS_INDEX_TABLE, (&index));
for (const fts_index_selector_t *s= fts_index_selector; s->suffix; s++)
{
fts_table.suffix= s->suffix;
fts_get_table_name(&fts_table, table_name, false);
if (dberr_t err= fts_lock_table(trx, table_name))
return err;
}
return DB_SUCCESS;
}
/** Lock the internal common FTS_ tables, before fts_drop_common_tables().
@param trx transaction
@param table table containing FULLTEXT INDEX
@return DB_SUCCESS or error code */
dberr_t fts_lock_common_tables(trx_t *trx, const dict_table_t &table)
{
fts_table_t fts_table;
char table_name[MAX_FULL_NAME_LEN];
FTS_INIT_FTS_TABLE(&fts_table, nullptr, FTS_COMMON_TABLE, (&table));
for (const char **suffix= fts_common_tables; *suffix; suffix++)
{
fts_table.suffix= *suffix;
fts_get_table_name(&fts_table, table_name, false);
if (dberr_t err= fts_lock_table(trx, table_name))
return err;
}
return DB_SUCCESS;
}
/** Lock the internal FTS_ tables for table, before fts_drop_tables().
@param trx transaction
@param table table containing FULLTEXT INDEX
@return DB_SUCCESS or error code */
dberr_t fts_lock_tables(trx_t *trx, const dict_table_t &table)
{
if (dberr_t err= fts_lock_common_tables(trx, table))
return err;
if (!table.fts)
return DB_SUCCESS;
auto indexes= table.fts->indexes;
if (!indexes)
return DB_SUCCESS;
for (ulint i= 0; i < ib_vector_size(indexes); ++i)
if (dberr_t err=
fts_lock_index_tables(trx, *static_cast<const dict_index_t*>
(ib_vector_getp(indexes, i))))
return err;
return DB_SUCCESS;
}
/** Drops the common ancillary tables needed for supporting an FTS index /** Drops the common ancillary tables needed for supporting an FTS index
on the given table. row_mysql_lock_data_dictionary must have been called on the given table. row_mysql_lock_data_dictionary must have been called
before this. before this.
@param[in] trx transaction to drop fts common table @param trx transaction to drop fts common table
@param[in] fts_table table with an FTS index @param fts_table table with an FTS index
@param rename whether to rename before dropping
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
static dberr_t fts_drop_common_tables(trx_t *trx, fts_table_t *fts_table) static dberr_t fts_drop_common_tables(trx_t *trx, fts_table_t *fts_table,
bool rename)
{ {
ulint i;
dberr_t error = DB_SUCCESS; dberr_t error = DB_SUCCESS;
for (i = 0; fts_common_tables[i] != NULL; ++i) { for (ulint i = 0; fts_common_tables[i] != NULL; ++i) {
dberr_t err; dberr_t err;
char table_name[MAX_FULL_NAME_LEN]; char table_name[MAX_FULL_NAME_LEN];
fts_table->suffix = fts_common_tables[i]; fts_table->suffix = fts_common_tables[i];
fts_get_table_name(fts_table, table_name, true); fts_get_table_name(fts_table, table_name, true);
err = fts_drop_table(trx, table_name); err = fts_drop_table(trx, table_name, rename);
/* We only return the status of the last error. */ /* We only return the status of the last error. */
if (err != DB_SUCCESS && err != DB_FAIL) { if (err != DB_SUCCESS && err != DB_FAIL) {
@@ -1571,17 +1661,13 @@ static dberr_t fts_drop_common_tables(trx_t *trx, fts_table_t *fts_table)
/****************************************************************//** /****************************************************************//**
Drops FTS auxiliary tables for an FTS index Drops FTS auxiliary tables for an FTS index
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
dberr_t dberr_t fts_drop_index_tables(trx_t *trx, const dict_index_t &index)
fts_drop_index_tables(
trx_t* trx, /*!< in: transaction */
dict_index_t* index) /*!< in: fts instance */
{ {
ulint i; ulint i;
fts_table_t fts_table; fts_table_t fts_table;
dberr_t error = DB_SUCCESS; dberr_t error = DB_SUCCESS;
FTS_INIT_INDEX_TABLE(&fts_table, NULL, FTS_INDEX_TABLE, index); FTS_INIT_INDEX_TABLE(&fts_table, nullptr, FTS_INDEX_TABLE, (&index));
for (i = 0; i < FTS_NUM_AUX_INDEX; ++i) { for (i = 0; i < FTS_NUM_AUX_INDEX; ++i) {
dberr_t err; dberr_t err;
@@ -1590,7 +1676,7 @@ fts_drop_index_tables(
fts_table.suffix = fts_get_suffix(i); fts_table.suffix = fts_get_suffix(i);
fts_get_table_name(&fts_table, table_name, true); fts_get_table_name(&fts_table, table_name, true);
err = fts_drop_table(trx, table_name); err = fts_drop_table(trx, table_name, false);
/* We only return the status of the last error. */ /* We only return the status of the last error. */
if (err != DB_SUCCESS && err != DB_FAIL) { if (err != DB_SUCCESS && err != DB_FAIL) {
@@ -1611,52 +1697,36 @@ dberr_t
fts_drop_all_index_tables( fts_drop_all_index_tables(
/*======================*/ /*======================*/
trx_t* trx, /*!< in: transaction */ trx_t* trx, /*!< in: transaction */
fts_t* fts) /*!< in: fts instance */ const fts_t* fts) /*!< in: fts instance */
{ {
dberr_t error = DB_SUCCESS; dberr_t error= DB_SUCCESS;
auto indexes= fts->indexes;
if (!indexes)
return DB_SUCCESS;
for (ulint i = 0; for (ulint i= 0; i < ib_vector_size(indexes); ++i)
fts->indexes != 0 && i < ib_vector_size(fts->indexes); if (dberr_t err= fts_drop_index_tables(trx,
++i) { *static_cast<const dict_index_t*>
(ib_vector_getp(indexes, i))))
dberr_t err; error= err;
dict_index_t* index; return error;
index = static_cast<dict_index_t*>(
ib_vector_getp(fts->indexes, i));
err = fts_drop_index_tables(trx, index);
if (err != DB_SUCCESS) {
error = err;
}
}
return(error);
} }
/*********************************************************************//** /** Drop the internal FTS_ tables for table.
Drops the ancillary tables needed for supporting an FTS index on a @param trx transaction
given table. row_mysql_lock_data_dictionary must have been called before @param table table containing FULLTEXT INDEX
this.
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
dberr_t dberr_t fts_drop_tables(trx_t *trx, const dict_table_t &table)
fts_drop_tables(
/*============*/
trx_t* trx, /*!< in: transaction */
dict_table_t* table) /*!< in: table has the FTS index */
{ {
dberr_t error; dberr_t error;
fts_table_t fts_table; fts_table_t fts_table;
FTS_INIT_FTS_TABLE(&fts_table, NULL, FTS_COMMON_TABLE, table); FTS_INIT_FTS_TABLE(&fts_table, NULL, FTS_COMMON_TABLE, (&table));
/* TODO: This is not atomic and can cause problems during recovery. */ error = fts_drop_common_tables(trx, &fts_table, false);
error = fts_drop_common_tables(trx, &fts_table); if (error == DB_SUCCESS && table.fts) {
error = fts_drop_all_index_tables(trx, table.fts);
if (error == DB_SUCCESS && table->fts) {
error = fts_drop_all_index_tables(trx, table->fts);
} }
return(error); return(error);
@@ -1797,7 +1867,7 @@ fts_create_common_tables(
FTS_INIT_FTS_TABLE(&fts_table, NULL, FTS_COMMON_TABLE, table); FTS_INIT_FTS_TABLE(&fts_table, NULL, FTS_COMMON_TABLE, table);
error = fts_drop_common_tables(trx, &fts_table); error = fts_drop_common_tables(trx, &fts_table, true);
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
@@ -4172,8 +4242,7 @@ begin_sync:
index_cache = static_cast<fts_index_cache_t*>( index_cache = static_cast<fts_index_cache_t*>(
ib_vector_get(cache->indexes, i)); ib_vector_get(cache->indexes, i));
if (index_cache->index->to_be_dropped if (index_cache->index->to_be_dropped) {
|| index_cache->index->table->to_be_dropped) {
continue; continue;
} }
@@ -4201,7 +4270,6 @@ begin_sync:
ib_vector_get(cache->indexes, i)); ib_vector_get(cache->indexes, i));
if (index_cache->index->to_be_dropped if (index_cache->index->to_be_dropped
|| index_cache->index->table->to_be_dropped
|| fts_sync_index_check(index_cache)) { || fts_sync_index_check(index_cache)) {
continue; continue;
} }

View File

@@ -94,7 +94,6 @@ this program; if not, write to the Free Software Foundation, Inc.,
#include "mtr0mtr.h" #include "mtr0mtr.h"
#include "os0file.h" #include "os0file.h"
#include "page0zip.h" #include "page0zip.h"
#include "pars0pars.h"
#include "rem0types.h" #include "rem0types.h"
#include "row0import.h" #include "row0import.h"
#include "row0ins.h" #include "row0ins.h"
@@ -186,7 +185,7 @@ static char* innobase_reset_all_monitor_counter;
stopword table to be used */ stopword table to be used */
static char* innobase_server_stopword_table; static char* innobase_server_stopword_table;
static my_bool innobase_rollback_on_timeout; my_bool innobase_rollback_on_timeout;
static my_bool innobase_create_status_file; static my_bool innobase_create_status_file;
my_bool innobase_stats_on_metadata; my_bool innobase_stats_on_metadata;
static my_bool innodb_optimize_fulltext_only; static my_bool innodb_optimize_fulltext_only;
@@ -1312,7 +1311,8 @@ static ibool innodb_drop_database_fk(void *node, void *report)
return true; return true;
} }
/** Remove all tables in the named database inside InnoDB. /** After DROP DATABASE executed ha_innobase::delete_table() on all
tables that it was aware of, drop any leftover tables inside InnoDB.
@param path database path */ @param path database path */
static void innodb_drop_database(handlerton*, char *path) static void innodb_drop_database(handlerton*, char *path)
{ {
@@ -1375,15 +1375,12 @@ retry:
continue; continue;
const auto n_handles= table->get_ref_count(); const auto n_handles= table->get_ref_count();
const bool locks= !n_handles && lock_table_has_locks(table); const bool locks= !n_handles && lock_table_has_locks(table);
const auto n_fk_checks= table->n_foreign_key_checks_running; if (n_handles || locks)
if (n_fk_checks || n_handles || locks)
{ {
err= DB_ERROR; err= DB_ERROR;
ib::error errmsg; ib::error errmsg;
errmsg << "DROP DATABASE: cannot DROP TABLE " << table->name; errmsg << "DROP DATABASE: cannot DROP TABLE " << table->name;
if (n_fk_checks) if (n_handles)
errmsg << " due to " << n_fk_checks << " FOREIGN KEY checks";
else if (n_handles)
errmsg << " due to " << n_handles << " open handles"; errmsg << " due to " << n_handles << " open handles";
else else
errmsg << " due to locks"; errmsg << " due to locks";
@@ -1393,6 +1390,20 @@ retry:
} }
} }
trx_start_for_ddl(trx);
uint errors= 0;
char db[NAME_LEN + 1];
strconvert(&my_charset_filename, namebuf, len, system_charset_info, db,
sizeof db, &errors);
if (errors);
else if (dict_stats_delete(db, trx))
{
/* Ignore this error. Leaving garbage statistics behind is a
lesser evil. Carry on to try to remove any garbage tables. */
trx->rollback();
trx_start_for_ddl(trx);
}
static const char drop_database[] = static const char drop_database[] =
"PROCEDURE DROP_DATABASE_PROC () IS\n" "PROCEDURE DROP_DATABASE_PROC () IS\n"
"fk CHAR;\n" "fk CHAR;\n"
@@ -1453,7 +1464,6 @@ retry:
"END;\n"; "END;\n";
innodb_drop_database_fk_report report{{namebuf, len + 1}, false}; innodb_drop_database_fk_report report{{namebuf, len + 1}, false};
trx_start_for_ddl(trx);
if (err == DB_SUCCESS) if (err == DB_SUCCESS)
{ {
@@ -1539,8 +1549,7 @@ retry:
ut_ad("corrupted SYS_TABLES.SPACE" == 0); ut_ad("corrupted SYS_TABLES.SPACE" == 0);
else if (uint32_t space_id= mach_read_from_4(s)) else if (uint32_t space_id= mach_read_from_4(s))
{ {
pfs_os_file_t detached= OS_FILE_CLOSED; pfs_os_file_t detached= fil_delete_tablespace(space_id);
fil_delete_tablespace(space_id, true, &detached);
if (detached != OS_FILE_CLOSED) if (detached != OS_FILE_CLOSED)
to_close.emplace_back(detached); to_close.emplace_back(detached);
} }
@@ -1900,6 +1909,9 @@ static int innobase_wsrep_set_checkpoint(handlerton* hton, const XID* xid);
static int innobase_wsrep_get_checkpoint(handlerton* hton, XID* xid); static int innobase_wsrep_get_checkpoint(handlerton* hton, XID* xid);
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
#define normalize_table_name(a,b) \
normalize_table_name_c_low(a,b,IF_WIN(true,false))
ulonglong ha_innobase::table_version() const ulonglong ha_innobase::table_version() const
{ {
/* This is either "garbage" or something that was assigned /* This is either "garbage" or something that was assigned
@@ -2030,7 +2042,7 @@ convert_error_code_to_mysql(
if (thd) { if (thd) {
thd_mark_transaction_to_rollback( thd_mark_transaction_to_rollback(
thd, (bool) row_rollback_on_timeout); thd, innobase_rollback_on_timeout);
} }
return(HA_ERR_LOCK_WAIT_TIMEOUT); return(HA_ERR_LOCK_WAIT_TIMEOUT);
@@ -2066,9 +2078,6 @@ convert_error_code_to_mysql(
"InnoDB"); "InnoDB");
return(HA_ERR_INTERNAL_ERROR); return(HA_ERR_INTERNAL_ERROR);
case DB_TABLE_IN_FK_CHECK:
return(HA_ERR_TABLE_IN_FK_CHECK);
case DB_TABLE_NOT_FOUND: case DB_TABLE_NOT_FOUND:
return(HA_ERR_NO_SUCH_TABLE); return(HA_ERR_NO_SUCH_TABLE);
@@ -3809,8 +3818,6 @@ static int innodb_init_params()
srv_buf_pool_size = ulint(innobase_buffer_pool_size); srv_buf_pool_size = ulint(innobase_buffer_pool_size);
row_rollback_on_timeout = (ibool) innobase_rollback_on_timeout;
if (innobase_open_files < 10) { if (innobase_open_files < 10) {
innobase_open_files = 300; innobase_open_files = 300;
if (srv_file_per_table && tc_size > 300 && tc_size < open_files_limit) { if (srv_file_per_table && tc_size > 300 && tc_size < open_files_limit) {
@@ -3943,6 +3950,7 @@ static int innodb_init(void* p)
HTON_NATIVE_SYS_VERSIONING | HTON_NATIVE_SYS_VERSIONING |
HTON_WSREP_REPLICATION | HTON_WSREP_REPLICATION |
HTON_REQUIRES_CLOSE_AFTER_TRUNCATE | HTON_REQUIRES_CLOSE_AFTER_TRUNCATE |
HTON_TRUNCATE_REQUIRES_EXCLUSIVE_USE |
HTON_REQUIRES_NOTIFY_TABLEDEF_CHANGED_AFTER_COMMIT; HTON_REQUIRES_NOTIFY_TABLEDEF_CHANGED_AFTER_COMMIT;
#ifdef WITH_WSREP #ifdef WITH_WSREP
@@ -4129,9 +4137,8 @@ innobase_commit_low(
#ifdef WITH_WSREP #ifdef WITH_WSREP
const char* tmp = 0; const char* tmp = 0;
const bool is_wsrep = trx->is_wsrep(); const bool is_wsrep = trx->is_wsrep();
THD* thd = trx->mysql_thd;
if (is_wsrep) { if (is_wsrep) {
tmp = thd_proc_info(thd, "innobase_commit_low()"); tmp = thd_proc_info(trx->mysql_thd, "innobase_commit_low()");
} }
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
if (trx_is_started(trx)) { if (trx_is_started(trx)) {
@@ -4145,7 +4152,7 @@ innobase_commit_low(
#ifdef WITH_WSREP #ifdef WITH_WSREP
if (is_wsrep) { if (is_wsrep) {
thd_proc_info(thd, tmp); thd_proc_info(trx->mysql_thd, tmp);
} }
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
} }
@@ -5009,10 +5016,6 @@ ha_innobase::table_cache_type()
return(HA_CACHE_TBL_ASKTRANSACT); return(HA_CACHE_TBL_ASKTRANSACT);
} }
/****************************************************************//**
Determines if the primary key is clustered index.
@return true */
/** Normalizes a table name string. /** Normalizes a table name string.
A normalized name consists of the database name catenated to '/' A normalized name consists of the database name catenated to '/'
and table name. For example: test/mytable. and table name. For example: test/mytable.
@@ -5027,7 +5030,7 @@ normalize_table_name_c_low(
char* norm_name, /* out: normalized name as a char* norm_name, /* out: normalized name as a
null-terminated string */ null-terminated string */
const char* name, /* in: table name string */ const char* name, /* in: table name string */
ibool set_lower_case) /* in: TRUE if we want to set bool set_lower_case) /* in: TRUE if we want to set
name to lower case */ name to lower case */
{ {
char* name_ptr; char* name_ptr;
@@ -5095,29 +5098,11 @@ create_table_info_t::create_table_info_t(
m_default_row_format(innodb_default_row_format), m_default_row_format(innodb_default_row_format),
m_create_info(create_info), m_create_info(create_info),
m_table_name(table_name), m_table(NULL), m_table_name(table_name), m_table(NULL),
m_drop_before_rollback(false),
m_remote_path(remote_path), m_remote_path(remote_path),
m_innodb_file_per_table(file_per_table) m_innodb_file_per_table(file_per_table)
{ {
} }
/** Normalizes a table name string.
A normalized name consists of the database name catenated to '/'
and table name. For example: test/mytable.
On Windows, normalization puts both the database name and the
table name always to lower case if "set_lower_case" is set to TRUE.
@param[out] norm_name Normalized name, null-terminated.
@param[in] name Name to normalize.
@param[in] set_lower_case True if we also should fold to lower case. */
void
create_table_info_t::normalize_table_name_low(
char* norm_name,
const char* name,
ibool set_lower_case)
{
normalize_table_name_c_low(norm_name, name, set_lower_case);
}
#if !defined(DBUG_OFF) #if !defined(DBUG_OFF)
/********************************************************************* /*********************************************************************
Test normalize_table_name_low(). */ Test normalize_table_name_low(). */
@@ -5172,7 +5157,7 @@ test_normalize_table_name_low()
" testing \"%s\", expected \"%s\"... ", " testing \"%s\", expected \"%s\"... ",
test_data[i][0], test_data[i][1]); test_data[i][0], test_data[i][1]);
create_table_info_t::normalize_table_name_low( normalize_table_name_c_low(
norm_name, test_data[i][0], FALSE); norm_name, test_data[i][0], FALSE);
if (strcmp(norm_name, test_data[i][1]) == 0) { if (strcmp(norm_name, test_data[i][1]) == 0) {
@@ -6040,10 +6025,8 @@ ha_innobase::open_dict_table(
whether there exists table name in whether there exists table name in
system table whose name is system table whose name is
not being normalized to lower case */ not being normalized to lower case */
create_table_info_t:: normalize_table_name_c_low(
normalize_table_name_low( par_case_name, table_name, false);
par_case_name,
table_name, FALSE);
#endif #endif
ib_table = dict_table_open_on_name( ib_table = dict_table_open_on_name(
par_case_name, FALSE, TRUE, par_case_name, FALSE, TRUE,
@@ -10395,7 +10378,6 @@ create_table_info_t::create_table_def()
DBUG_PRINT("enter", ("table_name: %s", m_table_name)); DBUG_PRINT("enter", ("table_name: %s", m_table_name));
DBUG_ASSERT(m_trx->mysql_thd == m_thd); DBUG_ASSERT(m_trx->mysql_thd == m_thd);
DBUG_ASSERT(!m_drop_before_rollback);
/* MySQL does the name length check. But we do additional check /* MySQL does the name length check. But we do additional check
on the name length here */ on the name length here */
@@ -10672,7 +10654,6 @@ err_col:
} else { } else {
if (err == DB_SUCCESS) { if (err == DB_SUCCESS) {
err = row_create_table_for_mysql(table, m_trx); err = row_create_table_for_mysql(table, m_trx);
m_drop_before_rollback = (err == DB_SUCCESS);
} }
DBUG_EXECUTE_IF("ib_crash_during_create_for_encryption", DBUG_EXECUTE_IF("ib_crash_during_create_for_encryption",
@@ -12493,9 +12474,6 @@ int create_table_info_t::create_table(bool create_fk)
DBUG_RETURN(error); DBUG_RETURN(error);
} }
DBUG_ASSERT(m_drop_before_rollback
== !(m_flags2 & DICT_TF2_TEMPORARY));
/* Create the keys */ /* Create the keys */
if (m_form->s->keys == 0 || primary_key_no == -1) { if (m_form->s->keys == 0 || primary_key_no == -1) {
@@ -13044,18 +13022,15 @@ ha_innobase::create(
/* Drop the being-created table before rollback, /* Drop the being-created table before rollback,
so that rollback can possibly rename back a table so that rollback can possibly rename back a table
that could have been renamed before the failed creation. */ that could have been renamed before the failed creation. */
if (info.drop_before_rollback()) {
trx->error_state = DB_SUCCESS;
row_drop_table_for_mysql(info.table_name(),
trx, SQLCOM_TRUNCATE, true,
false);
}
trx_rollback_for_mysql(trx); trx_rollback_for_mysql(trx);
row_mysql_unlock_data_dictionary(trx); row_mysql_unlock_data_dictionary(trx);
} else { } else {
innobase_commit_low(trx); /* When this is invoked as part of ha_innobase::truncate(),
the old copy of the table will be deleted here. */
std::vector<pfs_os_file_t> deleted;
trx->commit(deleted);
row_mysql_unlock_data_dictionary(trx); row_mysql_unlock_data_dictionary(trx);
ut_ad(!srv_read_only_mode); for (pfs_os_file_t d : deleted) os_file_close(d);
error = info.create_table_update_dict(); error = info.create_table_update_dict();
} }
@@ -13142,7 +13117,7 @@ ha_innobase::discard_or_import_tablespace(
} }
err = row_discard_tablespace_for_mysql( err = row_discard_tablespace_for_mysql(
m_prebuilt->table->name.m_name, m_prebuilt->trx); m_prebuilt->table, m_prebuilt->trx);
} else if (m_prebuilt->table->is_readable()) { } else if (m_prebuilt->table->is_readable()) {
/* Commit the transaction in order to /* Commit the transaction in order to
@@ -13222,141 +13197,210 @@ ha_innobase::discard_or_import_tablespace(
} }
/** /** DROP TABLE (possibly as part of DROP DATABASE, CREATE/ALTER TABLE)
Drops a table from an InnoDB database. Before calling this function, @param name table name
MySQL calls innobase_commit to commit the transaction of the current user.
Then the current user cannot have locks set on the table. Drop table
operation inside InnoDB will remove all locks any user has on the table
inside InnoDB.
@param[in] name table name
@param[in] sqlcom SQLCOM_DROP_DB, SQLCOM_TRUNCATE, ...
@return error number */ @return error number */
inline int ha_innobase::delete_table(const char* name, enum_sql_command sqlcom) int ha_innobase::delete_table(const char *name)
{ {
dberr_t err; DBUG_ENTER("ha_innobase::delete_table");
THD* thd = ha_thd(); if (high_level_read_only)
char norm_name[FN_REFLEN]; DBUG_RETURN(HA_ERR_TABLE_READONLY);
DBUG_ENTER("ha_innobase::delete_table"); THD *thd= ha_thd();
DBUG_EXECUTE_IF( DBUG_EXECUTE_IF("test_normalize_table_name_low",
"test_normalize_table_name_low", test_normalize_table_name_low(););
test_normalize_table_name_low(); DBUG_EXECUTE_IF("test_ut_format_name", test_ut_format_name(););
);
DBUG_EXECUTE_IF(
"test_ut_format_name",
test_ut_format_name();
);
/* Strangely, MySQL passes the table name without the '.frm' trx_t *parent_trx= check_trx_exists(thd);
extension, in contrast to ::create */ dict_table_t *table;
normalize_table_name(norm_name, name);
if (high_level_read_only) { for (;;)
DBUG_RETURN(HA_ERR_TABLE_READONLY); {
} char norm_name[FN_REFLEN];
normalize_table_name(norm_name, name);
span<const char> n{norm_name, strlen(norm_name)};
trx_t* parent_trx = check_trx_exists(thd); dict_sys.lock(SRW_LOCK_CALL);
table= dict_sys.load_table(n, DICT_ERR_IGNORE_DROP);
/* Remove the to-be-dropped table from the list of modified tables #ifdef WITH_PARTITION_STORAGE_ENGINE
by parent_trx. Otherwise we may end up with an orphaned pointer to if (!table && lower_case_table_names == 1 && is_partition(norm_name))
the table object from parent_trx::mod_tables. This could happen in: {
SET AUTOCOMMIT=0; IF_WIN(normalize_table_name_c_low(norm_name, name, false),
CREATE TABLE t (PRIMARY KEY (a)) ENGINE=INNODB SELECT 1 AS a UNION innobase_casedn_str(norm_name));
ALL SELECT 1 AS a; */ table= dict_sys.load_table(n, DICT_ERR_IGNORE_DROP);
for (auto iter = parent_trx->mod_tables.begin(); }
iter != parent_trx->mod_tables.end();
++iter) {
dict_table_t* table_to_drop = iter->first;
if (strcmp(norm_name, table_to_drop->name.m_name) == 0) {
parent_trx->mod_tables.erase(iter);
break;
}
}
trx_t* trx = innobase_trx_allocate(thd);
ulint name_len = strlen(name);
ut_a(name_len < 1000);
trx->will_lock = true;
/* Drop the table in InnoDB */
err = row_drop_table_for_mysql(norm_name, trx, sqlcom);
if (err == DB_TABLE_NOT_FOUND && lower_case_table_names == 1) {
char* is_part = is_partition(norm_name);
if (is_part) {
char par_case_name[FN_REFLEN];
#ifndef _WIN32
/* Check for the table using lower
case name, including the partition
separator "P" */
strcpy(par_case_name, norm_name);
innobase_casedn_str(par_case_name);
#else
/* On Windows platfrom, check
whether there exists table name in
system table whose name is
not being normalized to lower case */
normalize_table_name_c_low(
par_case_name, name, FALSE);
#endif #endif
err = row_drop_table_for_mysql( if (!table)
par_case_name, trx, sqlcom); {
} dict_sys.unlock();
} DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
}
ut_ad(!srv_read_only_mode); if (dict_stats_stop_bg(table))
break;
dict_sys.unlock();
}
innobase_commit_low(trx); if (table->is_temporary())
{
dict_sys.remove(table, false, true);
dict_sys.unlock();
parent_trx->mod_tables.erase(table); /* CREATE...SELECT error handling */
btr_drop_temporary_table(*table);
dict_mem_table_free(table);
DBUG_RETURN(0);
}
trx->free(); table->acquire();
dict_sys.unlock();
DBUG_RETURN(convert_error_code_to_mysql(err, 0, NULL)); trx_t *trx= parent_trx;
} if (!trx->lock.table_locks.empty() &&
thd_sql_command(trx->mysql_thd) == SQLCOM_CREATE_TABLE)
{
#if 0 // MDEV-21602 FIXME: this fails for innodb.innodb and some others
for (const lock_t *l : trx->lock.table_locks)
if (l && l->type_mode == (LOCK_IX | LOCK_TABLE) &&
l->un_member.tab_lock.table == table)
goto create_select;
sql_print_warning("InnoDB: CREATE...SELECT did not hold expected locks");
create_select:
#endif
/* CREATE TABLE...PRIMARY KEY...SELECT ought to be dropping the
table because a duplicate key was detected. We shall hijack the
existing transaction to drop the table and commit the transaction.
If this is a partitioned table, one partition will use this hijacked
transaction; others will use a separate transaction, one per partition. */
ut_ad(!trx->dict_operation_lock_mode);
ut_ad(trx->will_lock);
ut_ad(trx->state == TRX_STATE_ACTIVE);
trx->dict_operation= true;
trx->internal= true;
}
else
{
trx= innobase_trx_allocate(thd);
trx_start_for_ddl(trx);
}
/** Drop an InnoDB table. dberr_t err= lock_table_for_trx(table, trx, LOCK_X);
@param[in] name table name const bool fts= err == DB_SUCCESS &&
@return error number */ (table->flags2 & (DICT_TF2_FTS_HAS_DOC_ID | DICT_TF2_FTS));
int ha_innobase::delete_table(const char* name)
{
enum_sql_command sqlcom = enum_sql_command(thd_sql_command(ha_thd()));
/* SQLCOM_TRUNCATE should be passed via ha_innobase::truncate() only.
On client disconnect, when dropping temporary tables, the if (fts)
previous sqlcom would not be overwritten. In such a case, we {
will have thd_kill_level() != NOT_KILLED, !m_prebuilt can fts_optimize_remove_table(table);
hold, and sqlcom could be anything, including TRUNCATE. purge_sys.stop_FTS();
err= fts_lock_tables(trx, *table);
}
The sqlcom only matters for persistent tables; no persistent dict_sys.lock(SRW_LOCK_CALL);
metadata or FOREIGN KEY metadata is kept for temporary trx->dict_operation_lock_mode= RW_X_LATCH;
tables. Therefore, we relax the assertion. If there is a bug if (!table->release() && err == DB_SUCCESS)
that slips through this assertion due to !m_prebuilt, the {
worst impact should be that on DROP TABLE of a persistent /* Wait for purge threads to stop using the table. */
table, FOREIGN KEY constraints will be ignored and their for (uint n= 15;;)
metadata will not be removed. */ {
DBUG_ASSERT(sqlcom != SQLCOM_TRUNCATE row_mysql_unlock_data_dictionary(trx);
|| (thd_kill_level(ha_thd()) != THD_IS_NOT_KILLED std::this_thread::sleep_for(std::chrono::milliseconds(50));
&& (!m_prebuilt row_mysql_lock_data_dictionary(trx);
|| m_prebuilt->table->is_temporary())));
return delete_table(name, sqlcom); if (!--n)
{
err= DB_LOCK_WAIT_TIMEOUT;
break;
}
if (!table->get_ref_count())
break;
}
}
if (err != DB_SUCCESS)
{
err_exit:
trx->rollback();
trx->dict_operation_lock_mode= 0;
switch (err) {
case DB_CANNOT_DROP_CONSTRAINT:
case DB_LOCK_WAIT_TIMEOUT:
break;
default:
ib::error() << "DROP TABLE " << table->name << ": " << err;
}
table->stats_bg_flag= BG_STAT_NONE;
if (fts)
{
fts_optimize_add_table(table);
purge_sys.resume_FTS();
}
dict_sys.unlock();
if (trx != parent_trx)
trx->free();
DBUG_RETURN(convert_error_code_to_mysql(err, 0, NULL));
}
if (!table->no_rollback() && trx->check_foreigns)
{
const bool drop_db= thd_sql_command(thd) == SQLCOM_DROP_DB;
for (auto foreign : table->referenced_set)
{
/* We should allow dropping a referenced table if creating
that referenced table has failed for some reason. For example
if referenced table is created but it column types that are
referenced do not match. */
if (foreign->foreign_table == table ||
(drop_db &&
dict_tables_have_same_db(table->name.m_name,
foreign->foreign_table_name_lookup)))
continue;
mysql_mutex_lock(&dict_foreign_err_mutex);
rewind(dict_foreign_err_file);
ut_print_timestamp(dict_foreign_err_file);
fputs(" Cannot drop table ", dict_foreign_err_file);
ut_print_name(dict_foreign_err_file, trx, table->name.m_name);
fputs("\nbecause it is referenced by ", dict_foreign_err_file);
ut_print_name(dict_foreign_err_file, trx, foreign->foreign_table_name);
putc('\n', dict_foreign_err_file);
mysql_mutex_unlock(&dict_foreign_err_mutex);
err= DB_CANNOT_DROP_CONSTRAINT;
goto err_exit;
}
}
if (!table->no_rollback())
{
err= trx->drop_table_foreign(table->name);
if (err == DB_SUCCESS)
err= trx->drop_table_statistics(table->name);
if (err != DB_SUCCESS)
goto err_exit;
}
err= trx->drop_table(*table);
if (err != DB_SUCCESS)
goto err_exit;
std::vector<pfs_os_file_t> deleted;
trx->commit(deleted);
row_mysql_unlock_data_dictionary(trx);
if (trx != parent_trx)
trx->free();
for (pfs_os_file_t d : deleted)
os_file_close(d);
if (fts)
purge_sys.resume_FTS();
DBUG_RETURN(0);
} }
/** Rename an InnoDB table. /** Rename an InnoDB table.
@param[in,out] trx InnoDB data dictionary transaction @param[in,out] trx InnoDB data dictionary transaction
@param[in] from old table name @param[in] from old table name
@param[in] to new table name @param[in] to new table name
@param[in] commit whether to commit trx (and to enforce FOREIGN KEY) @param[in] use_fk whether to enforce FOREIGN KEY
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
inline dberr_t innobase_rename_table(trx_t *trx, const char *from, inline dberr_t innobase_rename_table(trx_t *trx, const char *from,
const char *to, bool commit) const char *to, bool use_fk)
{ {
dberr_t error; dberr_t error;
char norm_to[FN_REFLEN]; char norm_to[FN_REFLEN];
@@ -13375,14 +13419,7 @@ inline dberr_t innobase_rename_table(trx_t *trx, const char *from,
trx_start_if_not_started(trx, true); trx_start_if_not_started(trx, true);
ut_ad(trx->will_lock); ut_ad(trx->will_lock);
if (commit) { error = row_rename_table_for_mysql(norm_from, norm_to, trx, use_fk);
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations. */
row_mysql_lock_data_dictionary(trx);
}
error = row_rename_table_for_mysql(norm_from, norm_to, trx, commit,
commit);
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
if (error == DB_TABLE_NOT_FOUND if (error == DB_TABLE_NOT_FOUND
@@ -13402,13 +13439,12 @@ inline dberr_t innobase_rename_table(trx_t *trx, const char *from,
whether there exists table name in whether there exists table name in
system table whose name is system table whose name is
not being normalized to lower case */ not being normalized to lower case */
create_table_info_t::normalize_table_name_low( normalize_table_name_c_low(
par_case_name, from, FALSE); par_case_name, from, false);
#endif /* _WIN32 */ #endif /* _WIN32 */
trx_start_if_not_started(trx, true); trx_start_if_not_started(trx, true);
error = row_rename_table_for_mysql( error = row_rename_table_for_mysql(
par_case_name, norm_to, trx, par_case_name, norm_to, trx, false);
true, false);
} }
} }
@@ -13432,10 +13468,6 @@ inline dberr_t innobase_rename_table(trx_t *trx, const char *from,
} }
} }
if (commit) {
row_mysql_unlock_data_dictionary(trx);
}
DBUG_RETURN(error); DBUG_RETURN(error);
} }
@@ -13453,92 +13485,158 @@ int ha_innobase::truncate()
} }
HA_CREATE_INFO info; HA_CREATE_INFO info;
mem_heap_t* heap = mem_heap_create(1000);
dict_table_t* ib_table = m_prebuilt->table; dict_table_t* ib_table = m_prebuilt->table;
const auto update_time = ib_table->update_time;
const auto stored_lock = m_prebuilt->stored_select_lock_type;
info.init(); info.init();
update_create_info_from_table(&info, table); update_create_info_from_table(&info, table);
switch (dict_tf_get_rec_format(ib_table->flags)) {
case REC_FORMAT_REDUNDANT:
info.row_type = ROW_TYPE_REDUNDANT;
break;
case REC_FORMAT_COMPACT:
info.row_type = ROW_TYPE_COMPACT;
break;
case REC_FORMAT_COMPRESSED:
info.row_type = ROW_TYPE_COMPRESSED;
break;
case REC_FORMAT_DYNAMIC:
info.row_type = ROW_TYPE_DYNAMIC;
break;
}
const auto stored_lock = m_prebuilt->stored_select_lock_type;
trx_t* trx = innobase_trx_allocate(m_user_thd);
trx_start_for_ddl(trx);
if (ib_table->is_temporary()) { if (ib_table->is_temporary()) {
info.options|= HA_LEX_CREATE_TMP_TABLE; info.options|= HA_LEX_CREATE_TMP_TABLE;
} else { btr_drop_temporary_table(*ib_table);
dict_get_and_save_data_dir_path(ib_table, false); m_prebuilt->table = nullptr;
row_prebuilt_free(m_prebuilt, false);
m_prebuilt = nullptr;
my_free(m_upd_buf);
m_upd_buf = nullptr;
m_upd_buf_size = 0;
row_mysql_lock_data_dictionary(trx);
ib_table->release();
dict_sys.remove(ib_table, false, true);
int err = create(ib_table->name.m_name, table, &info, true,
trx);
if (!err) {
err = open(ib_table->name.m_name, 0, 0);
m_prebuilt->stored_select_lock_type = stored_lock;
}
trx->free();
#ifdef BTR_CUR_HASH_ADAPT
if (UT_LIST_GET_LEN(ib_table->freed_indexes)) {
ib_table->vc_templ = nullptr;
ib_table->id = 0;
DBUG_RETURN(err);
}
#endif /* BTR_CUR_HASH_ADAPT */
dict_mem_table_free(ib_table);
DBUG_RETURN(err);
} }
char* data_file_name = ib_table->data_dir_path; mem_heap_t* heap = mem_heap_create(1000);
if (data_file_name) {
info.data_file_name = data_file_name
= mem_heap_strdup(heap, data_file_name);
}
dict_get_and_save_data_dir_path(ib_table, false);
info.data_file_name = ib_table->data_dir_path;
const char* temp_name = dict_mem_create_temporary_tablename( const char* temp_name = dict_mem_create_temporary_tablename(
heap, ib_table->name.m_name, ib_table->id); heap, ib_table->name.m_name, ib_table->id);
const char* name = mem_heap_strdup(heap, ib_table->name.m_name); const char* name = mem_heap_strdup(heap, ib_table->name.m_name);
trx_t* trx = innobase_trx_allocate(m_user_thd);
trx->will_lock = true; dberr_t error = lock_table_for_trx(ib_table, trx, LOCK_X);
trx->dict_operation = true; const bool fts = error == DB_SUCCESS
&& ib_table->flags2 & (DICT_TF2_FTS_HAS_DOC_ID | DICT_TF2_FTS);
if (fts) {
fts_optimize_remove_table(ib_table);
purge_sys.stop_FTS();
error = fts_lock_tables(trx, *ib_table);
}
row_mysql_lock_data_dictionary(trx); row_mysql_lock_data_dictionary(trx);
dict_stats_wait_bg_to_stop_using_table(ib_table, trx); dict_stats_wait_bg_to_stop_using_table(ib_table, trx);
/* Wait for purge threads to stop using the table. */
int err = convert_error_code_to_mysql( for (uint n = 15; ib_table->get_ref_count() > 1; ) {
innobase_rename_table(trx, ib_table->name.m_name, temp_name, if (!--n) {
false), error = DB_LOCK_WAIT_TIMEOUT;
ib_table->flags, m_user_thd);
if (err) {
trx_rollback_for_mysql(trx);
row_mysql_unlock_data_dictionary(trx);
} else {
switch (dict_tf_get_rec_format(ib_table->flags)) {
case REC_FORMAT_REDUNDANT:
info.row_type = ROW_TYPE_REDUNDANT;
break;
case REC_FORMAT_COMPACT:
info.row_type = ROW_TYPE_COMPACT;
break;
case REC_FORMAT_COMPRESSED:
info.row_type = ROW_TYPE_COMPRESSED;
break;
case REC_FORMAT_DYNAMIC:
info.row_type = ROW_TYPE_DYNAMIC;
break; break;
} }
row_mysql_unlock_data_dictionary(trx);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
row_mysql_lock_data_dictionary(trx);
}
if (error == DB_SUCCESS) {
error = innobase_rename_table(trx, ib_table->name.m_name,
temp_name, false);
if (error == DB_SUCCESS) {
error = trx->drop_table(*ib_table);
}
}
int err = convert_error_code_to_mysql(error, ib_table->flags,
m_user_thd);
if (err) {
trx_rollback_for_mysql(trx);
if (fts) {
fts_optimize_add_table(ib_table);
purge_sys.resume_FTS();
}
row_mysql_unlock_data_dictionary(trx);
} else {
const auto update_time = ib_table->update_time;
const auto stored_lock = m_prebuilt->stored_select_lock_type;
ib_table->release();
m_prebuilt->table = nullptr;
err = create(name, table, &info, err = create(name, table, &info,
ib_table->is_temporary() dict_table_is_file_per_table(ib_table), trx);
|| dict_table_is_file_per_table(ib_table), trx); if (fts) {
purge_sys.resume_FTS();
}
if (err) {
reload:
m_prebuilt->table = dict_table_open_on_name(
name, false, false, DICT_ERR_IGNORE_NONE);
} else {
row_prebuilt_t* prebuilt = m_prebuilt;
uchar* upd_buf = m_upd_buf;
ulint upd_buf_size = m_upd_buf_size;
/* Mimic ha_innobase::close(). */
m_prebuilt = nullptr;
m_upd_buf = nullptr;
m_upd_buf_size = 0;
err = open(name, 0, 0);
if (!err) {
m_prebuilt->stored_select_lock_type
= stored_lock;
m_prebuilt->table->update_time = update_time;
row_prebuilt_free(prebuilt, false);
my_free(upd_buf);
} else {
/* Revert to the old table. */
m_prebuilt = prebuilt;
m_upd_buf = upd_buf;
m_upd_buf_size = upd_buf_size;
goto reload;
}
}
} }
trx->free(); trx->free();
if (!err) {
/* Reopen the newly created table, and drop the
original table that was renamed to temp_name. */
row_prebuilt_t* prebuilt = m_prebuilt;
uchar* upd_buf = m_upd_buf;
ulint upd_buf_size = m_upd_buf_size;
/* Mimic ha_innobase::close(). */
m_prebuilt = NULL;
m_upd_buf = NULL;
m_upd_buf_size = 0;
err = open(name, 0, 0);
if (!err) {
m_prebuilt->stored_select_lock_type = stored_lock;
m_prebuilt->table->update_time = update_time;
row_prebuilt_free(prebuilt, FALSE);
delete_table(temp_name, SQLCOM_TRUNCATE);
my_free(upd_buf);
} else {
/* Revert to the old table before truncation. */
m_prebuilt = prebuilt;
m_upd_buf = upd_buf;
m_upd_buf_size = upd_buf_size;
}
}
mem_heap_free(heap); mem_heap_free(heap);
DBUG_RETURN(err); DBUG_RETURN(err);
} }
@@ -13567,54 +13665,46 @@ ha_innobase::rename_table(
/* We are doing a DDL operation. */ /* We are doing a DDL operation. */
trx->will_lock = true; trx->will_lock = true;
trx->dict_operation = true; trx->dict_operation = true;
row_mysql_lock_data_dictionary(trx);
dberr_t error = innobase_rename_table(trx, from, to, true); dberr_t error = innobase_rename_table(trx, from, to, true);
DEBUG_SYNC(thd, "after_innobase_rename_table"); DEBUG_SYNC(thd, "after_innobase_rename_table");
innobase_commit_low(trx);
trx->free();
if (error == DB_SUCCESS) { if (error == DB_SUCCESS) {
char norm_from[MAX_FULL_NAME_LEN]; char norm_from[MAX_FULL_NAME_LEN];
char norm_to[MAX_FULL_NAME_LEN]; char norm_to[MAX_FULL_NAME_LEN];
char errstr[512];
dberr_t ret;
normalize_table_name(norm_from, from); normalize_table_name(norm_from, from);
normalize_table_name(norm_to, to); normalize_table_name(norm_to, to);
ret = dict_stats_rename_table(norm_from, norm_to, error = dict_stats_rename_table(norm_from, norm_to, trx);
errstr, sizeof(errstr)); if (error == DB_DUPLICATE_KEY) {
/* The duplicate may also occur in
if (ret != DB_SUCCESS) { mysql.innodb_index_stats. */
ib::error() << errstr; my_error(ER_DUP_KEY, MYF(0),
"mysql.innodb_table_stats");
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, error = DB_ERROR;
ER_LOCK_WAIT_TIMEOUT, errstr);
} }
} }
/* Add a special case to handle the Duplicated Key error if (error == DB_SUCCESS) {
and return DB_ERROR instead. innobase_commit_low(trx);
This is to avoid a possible SIGSEGV error from mysql error } else {
handling code. Currently, mysql handles the Duplicated Key trx->rollback();
error by re-entering the storage layer and getting dup key }
info by calling get_dup_key(). This operation requires a valid
table handle ('row_prebuilt_t' structure) which could no
longer be available in the error handling stage. The suggested
solution is to report a 'table exists' error message (since
the dup key error here is due to an existing table whose name
is the one we are trying to rename to) and return the generic
error code. */
if (error == DB_DUPLICATE_KEY) {
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), to);
row_mysql_unlock_data_dictionary(trx);
trx->free();
if (error == DB_DUPLICATE_KEY) {
/* We are not able to deal with handler::get_dup_key()
during DDL operations, because the duplicate key would
exist in metadata tables, not in the user table. */
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), to);
error = DB_ERROR; error = DB_ERROR;
} else if (error == DB_LOCK_WAIT_TIMEOUT) { } else if (error == DB_LOCK_WAIT_TIMEOUT) {
my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0), to); my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0), to);
error = DB_LOCK_WAIT; error = DB_LOCK_WAIT;
} }
@@ -17784,20 +17874,11 @@ innobase_fts_find_ranking(FT_INFO* fts_hdl, uchar*, uint)
} }
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
static my_bool innodb_background_drop_list_empty = TRUE;
static my_bool innodb_log_checkpoint_now = TRUE; static my_bool innodb_log_checkpoint_now = TRUE;
static my_bool innodb_buf_flush_list_now = TRUE; static my_bool innodb_buf_flush_list_now = TRUE;
static uint innodb_merge_threshold_set_all_debug static uint innodb_merge_threshold_set_all_debug
= DICT_INDEX_MERGE_THRESHOLD_DEFAULT; = DICT_INDEX_MERGE_THRESHOLD_DEFAULT;
/** Wait for the background drop list to become empty. */
static
void
wait_background_drop_list_empty(THD*, st_mysql_sys_var*, void*, const void*)
{
row_wait_for_background_drop_list_empty();
}
/****************************************************************//** /****************************************************************//**
Force innodb to checkpoint. */ Force innodb to checkpoint. */
static static
@@ -18341,12 +18422,6 @@ static MYSQL_SYSVAR_ULONG(io_capacity_max, srv_max_io_capacity,
SRV_MAX_IO_CAPACITY_LIMIT, 0); SRV_MAX_IO_CAPACITY_LIMIT, 0);
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
static MYSQL_SYSVAR_BOOL(background_drop_list_empty,
innodb_background_drop_list_empty,
PLUGIN_VAR_OPCMDARG,
"Wait for the background drop list to become empty",
NULL, wait_background_drop_list_empty, FALSE);
static MYSQL_SYSVAR_BOOL(log_checkpoint_now, innodb_log_checkpoint_now, static MYSQL_SYSVAR_BOOL(log_checkpoint_now, innodb_log_checkpoint_now,
PLUGIN_VAR_OPCMDARG, PLUGIN_VAR_OPCMDARG,
"Force checkpoint now", "Force checkpoint now",
@@ -19382,7 +19457,6 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(purge_threads), MYSQL_SYSVAR(purge_threads),
MYSQL_SYSVAR(purge_batch_size), MYSQL_SYSVAR(purge_batch_size),
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
MYSQL_SYSVAR(background_drop_list_empty),
MYSQL_SYSVAR(log_checkpoint_now), MYSQL_SYSVAR(log_checkpoint_now),
MYSQL_SYSVAR(buf_flush_list_now), MYSQL_SYSVAR(buf_flush_list_now),
MYSQL_SYSVAR(merge_threshold_set_all_debug), MYSQL_SYSVAR(merge_threshold_set_all_debug),

View File

@@ -204,8 +204,6 @@ public:
TABLE* form, TABLE* form,
HA_CREATE_INFO* create_info) override; HA_CREATE_INFO* create_info) override;
inline int delete_table(const char* name, enum_sql_command sqlcom);
int truncate() override; int truncate() override;
int delete_table(const char *name) override; int delete_table(const char *name) override;
@@ -708,26 +706,9 @@ public:
const char* table_name() const const char* table_name() const
{ return(m_table_name); } { return(m_table_name); }
/** @return whether the table needs to be dropped on rollback */
bool drop_before_rollback() const { return m_drop_before_rollback; }
THD* thd() const THD* thd() const
{ return(m_thd); } { return(m_thd); }
/** Normalizes a table name string.
A normalized name consists of the database name catenated to '/' and
table name. An example: test/mytable. On Windows normalization puts
both the database name and the table name always to lower case if
"set_lower_case" is set to true.
@param[in,out] norm_name Buffer to return the normalized name in.
@param[in] name Table name string.
@param[in] set_lower_case True if we want to set name to lower
case. */
static void normalize_table_name_low(
char* norm_name,
const char* name,
ibool set_lower_case);
private: private:
/** Parses the table name into normal name and either temp path or /** Parses the table name into normal name and either temp path or
remote path if needed.*/ remote path if needed.*/
@@ -757,8 +738,6 @@ private:
char* m_table_name; char* m_table_name;
/** Table */ /** Table */
dict_table_t* m_table; dict_table_t* m_table;
/** Whether the table needs to be dropped before rollback */
bool m_drop_before_rollback;
/** Remote path (DATA DIRECTORY) or zero length-string */ /** Remote path (DATA DIRECTORY) or zero length-string */
char* m_remote_path; char* m_remote_path;
@@ -861,15 +840,6 @@ innodb_base_col_setup_for_stored(
/** whether this is a stored generated column */ /** whether this is a stored generated column */
#define innobase_is_s_fld(field) ((field)->vcol_info && (field)->stored_in_db()) #define innobase_is_s_fld(field) ((field)->vcol_info && (field)->stored_in_db())
/** Always normalize table name to lower case on Windows */
#ifdef _WIN32
#define normalize_table_name(norm_name, name) \
create_table_info_t::normalize_table_name_low(norm_name, name, TRUE)
#else
#define normalize_table_name(norm_name, name) \
create_table_info_t::normalize_table_name_low(norm_name, name, FALSE)
#endif /* _WIN32 */
/** Converts a search mode flag understood by MySQL to a flag understood /** Converts a search mode flag understood by MySQL to a flag understood
by InnoDB. by InnoDB.
@param[in] find_flag MySQL search mode flag. @param[in] find_flag MySQL search mode flag.

File diff suppressed because it is too large Load Diff

View File

@@ -337,9 +337,9 @@ btr_create(
void btr_free_if_exists(fil_space_t *space, uint32_t page, void btr_free_if_exists(fil_space_t *space, uint32_t page,
index_id_t index_id, mtr_t *mtr); index_id_t index_id, mtr_t *mtr);
/** Free an index tree in a temporary tablespace. /** Drop a temporary table
@param[in] page_id root page id */ @param table temporary table */
void btr_free(const page_id_t page_id); void btr_drop_temporary_table(const dict_table_t &table);
/** Read the last used AUTO_INCREMENT value from PAGE_ROOT_AUTO_INC. /** Read the last used AUTO_INCREMENT value from PAGE_ROOT_AUTO_INC.
@param[in,out] index clustered index @param[in,out] index clustered index

View File

@@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2020, MariaDB Corporation. Copyright (c) 2017, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
@@ -128,7 +128,7 @@ void btr_search_update_hash_on_insert(btr_cur_t *cursor,
/** Updates the page hash index when a single record is deleted from a page. /** Updates the page hash index when a single record is deleted from a page.
@param[in] cursor cursor which was positioned on the record to delete @param[in] cursor cursor which was positioned on the record to delete
using btr_cur_search_, the record is not yet deleted.*/ using btr_cur_search_, the record is not yet deleted.*/
void btr_search_update_hash_on_delete(btr_cur_t* cursor); void btr_search_update_hash_on_delete(btr_cur_t *cursor);
/** Validates the search system. /** Validates the search system.
@return true if ok */ @return true if ok */

View File

@@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, 2020, MariaDB Corporation. Copyright (c) 2018, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
@@ -47,8 +47,7 @@ static inline btr_search_t* btr_search_info_create(mem_heap_t* heap)
/** Updates the search info. /** Updates the search info.
@param[in,out] info search info @param[in,out] info search info
@param[in,out] cursor cursor which was just positioned */ @param[in,out] cursor cursor which was just positioned */
void void btr_search_info_update_slow(btr_search_t *info, btr_cur_t *cursor);
btr_search_info_update_slow(btr_search_t* info, btr_cur_t* cursor);
/*********************************************************************//** /*********************************************************************//**
Updates the search info. */ Updates the search info. */
@@ -59,7 +58,10 @@ btr_search_info_update(
dict_index_t* index, /*!< in: index of the cursor */ dict_index_t* index, /*!< in: index of the cursor */
btr_cur_t* cursor) /*!< in: cursor which was just positioned */ btr_cur_t* cursor) /*!< in: cursor which was just positioned */
{ {
if (dict_index_is_spatial(index) || !btr_search_enabled) { ut_ad(!index->is_spatial());
ut_ad(!index->table->is_temporary());
if (!btr_search_enabled) {
return; return;
} }

View File

@@ -118,8 +118,6 @@ enum dberr_t {
DB_READ_ONLY, /*!< Update operation attempted in DB_READ_ONLY, /*!< Update operation attempted in
a read-only transaction */ a read-only transaction */
DB_FTS_INVALID_DOCID, /* FTS Doc ID cannot be zero */ DB_FTS_INVALID_DOCID, /* FTS Doc ID cannot be zero */
DB_TABLE_IN_FK_CHECK, /* table is being used in foreign
key check */
DB_ONLINE_LOG_TOO_BIG, /*!< Modification log grew too big DB_ONLINE_LOG_TOO_BIG, /*!< Modification log grew too big
during online index creation */ during online index creation */

View File

@@ -1000,15 +1000,6 @@ struct dict_index_t {
representation we add more columns */ representation we add more columns */
unsigned nulls_equal:1; unsigned nulls_equal:1;
/*!< if true, SQL NULL == SQL NULL */ /*!< if true, SQL NULL == SQL NULL */
#ifdef BTR_CUR_HASH_ADAPT
#ifdef MYSQL_INDEX_DISABLE_AHI
unsigned disable_ahi:1;
/*!< whether to disable the
adaptive hash index.
Maybe this could be disabled for
temporary tables? */
#endif
#endif /* BTR_CUR_HASH_ADAPT */
unsigned n_uniq:10;/*!< number of fields from the beginning unsigned n_uniq:10;/*!< number of fields from the beginning
which are enough to determine an index which are enough to determine an index
entry uniquely */ entry uniquely */
@@ -1958,23 +1949,6 @@ struct dict_table_t {
return versioned() && cols[vers_start].mtype == DATA_INT; return versioned() && cols[vers_start].mtype == DATA_INT;
} }
void inc_fk_checks()
{
#ifdef UNIV_DEBUG
int32_t fk_checks=
#endif
n_foreign_key_checks_running++;
ut_ad(fk_checks >= 0);
}
void dec_fk_checks()
{
#ifdef UNIV_DEBUG
int32_t fk_checks=
#endif
n_foreign_key_checks_running--;
ut_ad(fk_checks > 0);
}
/** For overflow fields returns potential max length stored inline */ /** For overflow fields returns potential max length stored inline */
inline size_t get_overflow_field_local_len() const; inline size_t get_overflow_field_local_len() const;
@@ -2049,7 +2023,7 @@ public:
table_id_t id; table_id_t id;
/** dict_sys.id_hash chain node */ /** dict_sys.id_hash chain node */
dict_table_t* id_hash; dict_table_t* id_hash;
/** Table name. */ /** Table name in name_hash */
table_name_t name; table_name_t name;
/** dict_sys.name_hash chain node */ /** dict_sys.name_hash chain node */
dict_table_t* name_hash; dict_table_t* name_hash;
@@ -2100,12 +2074,6 @@ public:
/** TRUE if the table object has been added to the dictionary cache. */ /** TRUE if the table object has been added to the dictionary cache. */
unsigned cached:1; unsigned cached:1;
/** TRUE if the table is to be dropped, but not yet actually dropped
(could in the background drop list). It is turned on at the beginning
of row_drop_table_for_mysql() and turned off just before we start to
update system tables for the drop. It is protected by dict_sys.latch. */
unsigned to_be_dropped:1;
/** Number of non-virtual columns defined so far. */ /** Number of non-virtual columns defined so far. */
unsigned n_def:10; unsigned n_def:10;
@@ -2202,11 +2170,6 @@ public:
loading child table into memory along with its parent table. */ loading child table into memory along with its parent table. */
byte fk_max_recusive_level; byte fk_max_recusive_level;
/** Count of how many foreign key check operations are currently being
performed on the table. We cannot drop the table while there are
foreign key checks running on it. */
Atomic_counter<int32_t> n_foreign_key_checks_running;
/** DDL transaction that last touched the table definition, or 0 if /** DDL transaction that last touched the table definition, or 0 if
no history is available. This includes possible changes in no history is available. This includes possible changes in
ha_innobase::prepare_inplace_alter_table() and ha_innobase::prepare_inplace_alter_table() and
@@ -2219,6 +2182,11 @@ public:
an empty leaf page), and an ahi_latch (if btr_search_enabled). */ an empty leaf page), and an ahi_latch (if btr_search_enabled). */
Atomic_relaxed<trx_id_t> bulk_trx_id; Atomic_relaxed<trx_id_t> bulk_trx_id;
/** Original table name, for MDL acquisition in purge. Normally,
this points to the same as name. When is_temporary_name(name.m_name) holds,
this should be a copy of the original table name, allocated from heap. */
table_name_t mdl_name;
/*!< set of foreign key constraints in the table; these refer to /*!< set of foreign key constraints in the table; these refer to
columns in other tables */ columns in other tables */
dict_foreign_set foreign_set; dict_foreign_set foreign_set;
@@ -2439,9 +2407,6 @@ public:
static dict_table_t *create(const span<const char> &name, fil_space_t *space, static dict_table_t *create(const span<const char> &name, fil_space_t *space,
ulint n_cols, ulint n_v_cols, ulint flags, ulint n_cols, ulint n_v_cols, ulint flags,
ulint flags2); ulint flags2);
/** @return whether SYS_TABLES.NAME is for a '#sql-ib' table */
static bool is_garbage_name(const void *data, size_t size);
}; };
inline void dict_index_t::set_modified(mtr_t& mtr) const inline void dict_index_t::set_modified(mtr_t& mtr) const

View File

@@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation. Copyright (c) 2017, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
@@ -64,10 +64,5 @@ dict_mem_fill_index_struct(
/* The '1 +' above prevents allocation /* The '1 +' above prevents allocation
of an empty mem block */ of an empty mem block */
index->nulls_equal = false; index->nulls_equal = false;
#ifdef BTR_CUR_HASH_ADAPT
#ifdef MYSQL_INDEX_DISABLE_AHI
index->disable_ahi = false;
#endif
#endif /* BTR_CUR_HASH_ADAPT */
ut_d(index->magic_n = DICT_INDEX_MAGIC_N); ut_d(index->magic_n = DICT_INDEX_MAGIC_N);
} }

View File

@@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 2009, 2018, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2009, 2018, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2020, MariaDB Corporation. Copyright (c) 2017, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
@@ -30,9 +30,6 @@ Created Jan 06, 2010 Vasil Dimov
#include "dict0types.h" #include "dict0types.h"
#include "trx0types.h" #include "trx0types.h"
#define TABLE_STATS_NAME "mysql/innodb_table_stats"
#define INDEX_STATS_NAME "mysql/innodb_index_stats"
enum dict_stats_upd_option_t { enum dict_stats_upd_option_t {
DICT_STATS_RECALC_PERSISTENT,/* (re) calculate the DICT_STATS_RECALC_PERSISTENT,/* (re) calculate the
statistics using a precise and slow statistics using a precise and slow
@@ -140,40 +137,31 @@ dict_stats_update(
the stats or to fetch them from the stats or to fetch them from
the persistent storage */ the persistent storage */
/** Remove the information for a particular index's stats from the persistent /** Execute DELETE FROM mysql.innodb_table_stats
storage if it exists and if there is data stored for this index. @param database_name database name
This function creates its own trx and commits it. @param table_name table name
@param trx transaction (nullptr=start and commit a new one)
We must modify system tables in a separate transaction in order to
adhere to the InnoDB design constraint that dict_sys.latch prevents
lock waits on system tables. If we modified system and user tables in
the same transaction, we should exclusively hold dict_sys.latch until
the transaction is committed, and effectively block other transactions
that will attempt to open any InnoDB tables. Because we have no
guarantee that user transactions will be committed fast, we cannot
afford to keep the system tables locked in a user transaction.
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
dberr_t dberr_t dict_stats_delete_from_table_stats(const char *database_name,
dict_stats_drop_index( const char *table_name,
/*==================*/ trx_t *trx= nullptr);
const char* tname, /*!< in: table name */ /** Execute DELETE FROM mysql.innodb_index_stats
const char* iname, /*!< in: index name */ @param database_name database name
char* errstr, /*!< out: error message if != DB_SUCCESS @param table_name table name
is returned */ @param trx transaction (nullptr=start and commit a new one)
ulint errstr_sz);/*!< in: size of the errstr buffer */
/*********************************************************************//**
Removes the statistics for a table and all of its indexes from the
persistent storage if it exists and if there is data stored for the table.
This function creates its own transaction and commits it.
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
dberr_t dberr_t dict_stats_delete_from_index_stats(const char *database_name,
dict_stats_drop_table( const char *table_name,
/*==================*/ trx_t *trx= nullptr);
const char* table_name, /*!< in: table name */ /** Execute DELETE FROM mysql.innodb_index_stats
char* errstr, /*!< out: error message @param database_name database name
if != DB_SUCCESS is returned */ @param table_name table name
ulint errstr_sz); /*!< in: size of errstr buffer */ @param index_name name of the index
@param trx transaction (nullptr=start and commit a new one)
@return DB_SUCCESS or error code */
dberr_t dict_stats_delete_from_index_stats(const char *database_name,
const char *table_name,
const char *index_name, trx_t *trx);
/*********************************************************************//** /*********************************************************************//**
Fetches or calculates new estimates for index statistics. */ Fetches or calculates new estimates for index statistics. */
@@ -183,31 +171,29 @@ dict_stats_update_for_index(
dict_index_t* index) /*!< in/out: index */ dict_index_t* index) /*!< in/out: index */
MY_ATTRIBUTE((nonnull)); MY_ATTRIBUTE((nonnull));
/*********************************************************************//** /** Rename a table in InnoDB persistent stats storage.
Renames a table in InnoDB persistent stats storage. @param old_name old table name
This function creates its own transaction and commits it. @param new_name new table name
@param trx transaction
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
dberr_t dberr_t dict_stats_rename_table(const char *old_name, const char *new_name,
dict_stats_rename_table( trx_t *trx);
/*====================*/ /** Rename an index in InnoDB persistent statistics.
const char* old_name, /*!< in: old table name */ @param db database name
const char* new_name, /*!< in: new table name */ @param table table name
char* errstr, /*!< out: error string if != DB_SUCCESS @param old_name old table name
is returned */ @param new_name new table name
size_t errstr_sz); /*!< in: errstr size */ @param trx transaction
/*********************************************************************//** @return DB_SUCCESS or error code */
Renames an index in InnoDB persistent stats storage. dberr_t dict_stats_rename_index(const char *db, const char *table,
This function creates its own transaction and commits it. const char *old_name, const char *new_name,
@return DB_SUCCESS or error code. DB_STATS_DO_NOT_EXIST will be returned trx_t *trx);
if the persistent stats do not exist. */
dberr_t /** Delete all persistent statistics for a database.
dict_stats_rename_index( @param db database name
/*====================*/ @param trx transaction
const dict_table_t* table, /*!< in: table whose index @return DB_SUCCESS or error code */
is renamed */ dberr_t dict_stats_delete(const char *db, trx_t *trx);
const char* old_index_name, /*!< in: old index name */
const char* new_index_name) /*!< in: new index name */
__attribute__((warn_unused_result));
/** Save an individual index's statistic into the persistent statistics /** Save an individual index's statistic into the persistent statistics
storage. storage.

View File

@@ -168,4 +168,7 @@ enum spatial_status_t {
SPATIAL_ONLY = 3 SPATIAL_ONLY = 3
}; };
#define TABLE_STATS_NAME "mysql/innodb_table_stats"
#define INDEX_STATS_NAME "mysql/innodb_index_stats"
#endif #endif

View File

@@ -518,8 +518,12 @@ public:
/** Close each file. Only invoked on fil_system.temp_space. */ /** Close each file. Only invoked on fil_system.temp_space. */
void close(); void close();
/** Note that operations on the tablespace must stop or can resume */ /** Note that operations on the tablespace must stop.
inline void set_stopping(bool stopping); @return whether the operations were already stopped */
inline bool set_stopping();
/** Note that operations on the tablespace can resume after truncation */
inline void clear_stopping();
/** Look up the tablespace and wait for pending operations to cease /** Look up the tablespace and wait for pending operations to cease
@param id tablespace identifier @param id tablespace identifier
@@ -1508,12 +1512,19 @@ inline void fil_space_t::reacquire()
#endif /* SAFE_MUTEX */ #endif /* SAFE_MUTEX */
} }
/** Note that operations on the tablespace must stop or can resume */ /** Note that operations on the tablespace must stop.
inline void fil_space_t::set_stopping(bool stopping) @return whether the operations were already stopped */
inline bool fil_space_t::set_stopping()
{ {
mysql_mutex_assert_owner(&fil_system.mutex); mysql_mutex_assert_owner(&fil_system.mutex);
ut_d(auto n=) n_pending.fetch_xor(STOPPING, std::memory_order_relaxed); return n_pending.fetch_or(STOPPING, std::memory_order_relaxed) & STOPPING;
ut_ad(!(n & STOPPING) == stopping); }
inline void fil_space_t::clear_stopping()
{
mysql_mutex_assert_owner(&fil_system.mutex);
ut_d(auto n=) n_pending.fetch_and(~STOPPING, std::memory_order_relaxed);
ut_ad(n & STOPPING);
} }
/** Flush pending writes from the file system cache to the file. */ /** Flush pending writes from the file system cache to the file. */
@@ -1595,13 +1606,12 @@ fil_write_flushed_lsn(
lsn_t lsn) lsn_t lsn)
MY_ATTRIBUTE((warn_unused_result)); MY_ATTRIBUTE((warn_unused_result));
MY_ATTRIBUTE((warn_unused_result))
/** Delete a tablespace and associated .ibd file. /** Delete a tablespace and associated .ibd file.
@param[in] id tablespace identifier @param id tablespace identifier
@param[in] if_exists whether to ignore missing tablespace @return detached file handle (to be closed by the caller)
@param[out] detached deatched file handle (if closing is not wanted) @return OS_FILE_CLOSED if no file existed */
@return DB_SUCCESS or error */ pfs_os_file_t fil_delete_tablespace(ulint id);
dberr_t fil_delete_tablespace(ulint id, bool if_exists= false,
pfs_os_file_t *detached= nullptr);
/** Close a single-table tablespace on failed IMPORT TABLESPACE. /** Close a single-table tablespace on failed IMPORT TABLESPACE.
The tablespace must be cached in the memory cache. The tablespace must be cached in the memory cache.

View File

@@ -500,17 +500,29 @@ fts_add_doc_id_column(
dict_table_t* table, /*!< in/out: Table with FTS index */ dict_table_t* table, /*!< in/out: Table with FTS index */
mem_heap_t* heap); /*!< in: temporary memory heap, or NULL */ mem_heap_t* heap); /*!< in: temporary memory heap, or NULL */
/*********************************************************************//** /** Lock the internal FTS_ tables for an index, before fts_drop_index_tables().
Drops the ancillary tables needed for supporting an FTS index on the @param trx transaction
given table. row_mysql_lock_data_dictionary must have been called before @param index fulltext index */
this. dberr_t fts_lock_index_tables(trx_t *trx, const dict_index_t &index);
/** Lock the internal common FTS_ tables, before fts_drop_common_tables().
@param trx transaction
@param table table containing FULLTEXT INDEX
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
dberr_t dberr_t fts_lock_common_tables(trx_t *trx, const dict_table_t &table);
fts_drop_tables(
/*============*/ /** Lock the internal FTS_ tables for table, before fts_drop_tables().
trx_t* trx, /*!< in: transaction */ @param trx transaction
dict_table_t* table); /*!< in: table has the FTS @param table table containing FULLTEXT INDEX
index */ @return DB_SUCCESS or error code */
dberr_t fts_lock_tables(trx_t *trx, const dict_table_t &table);
/** Drop the internal FTS_ tables for table.
@param trx transaction
@param table table containing FULLTEXT INDEX
@return DB_SUCCESS or error code */
dberr_t fts_drop_tables(trx_t *trx, const dict_table_t &table);
/******************************************************************//** /******************************************************************//**
The given transaction is about to be committed; do whatever is necessary The given transaction is about to be committed; do whatever is necessary
from the FTS system's POV. from the FTS system's POV.
@@ -643,11 +655,7 @@ fts_optimize_init(void);
/****************************************************************//** /****************************************************************//**
Drops index ancillary tables for a FTS index Drops index ancillary tables for a FTS index
@return DB_SUCCESS or error code */ @return DB_SUCCESS or error code */
dberr_t dberr_t fts_drop_index_tables(trx_t *trx, const dict_index_t &index)
fts_drop_index_tables(
/*==================*/
trx_t* trx, /*!< in: transaction */
dict_index_t* index) /*!< in: Index to drop */
MY_ATTRIBUTE((warn_unused_result)); MY_ATTRIBUTE((warn_unused_result));
/** Add the table to add to the OPTIMIZER's list. /** Add the table to add to the OPTIMIZER's list.

View File

@@ -453,7 +453,7 @@ normalize_table_name_c_low(
char* norm_name, /*!< out: normalized name as a char* norm_name, /*!< out: normalized name as a
null-terminated string */ null-terminated string */
const char* name, /*!< in: table name string */ const char* name, /*!< in: table name string */
ibool set_lower_case); /*!< in: TRUE if we want to set bool set_lower_case); /*!< in: true if we want to set
name to lower case */ name to lower case */
/** Create a MYSQL_THD for a background thread and mark it as such. /** Create a MYSQL_THD for a background thread and mark it as such.

View File

@@ -37,11 +37,6 @@ Created 9/17/2000 Heikki Tuuri
#include "fts0fts.h" #include "fts0fts.h"
#include "gis0type.h" #include "gis0type.h"
#include "sql_list.h"
#include "sql_cmd.h"
extern ibool row_rollback_on_timeout;
struct row_prebuilt_t; struct row_prebuilt_t;
class ha_innobase; class ha_innobase;
@@ -378,17 +373,6 @@ row_create_index_for_mysql(
uint32_t key_id) /*!< in: encryption key_id */ uint32_t key_id) /*!< in: encryption key_id */
MY_ATTRIBUTE((warn_unused_result)); MY_ATTRIBUTE((warn_unused_result));
/** The master task calls this regularly to drop tables which
we must drop in background after queries to them have ended.
@return how many tables dropped + remaining tables in list */
ulint row_drop_tables_for_mysql_in_background();
/** @return number of tables in the background drop list */
ulint row_get_background_drop_list_len_low();
/** Drop garbage tables during recovery. */
void row_mysql_drop_garbage_tables();
/*********************************************************************//** /*********************************************************************//**
Sets an exclusive lock on a table. Sets an exclusive lock on a table.
@return error code or DB_SUCCESS */ @return error code or DB_SUCCESS */
@@ -401,36 +385,12 @@ row_mysql_lock_table(
const char* op_info) /*!< in: string for trx->op_info */ const char* op_info) /*!< in: string for trx->op_info */
MY_ATTRIBUTE((nonnull, warn_unused_result)); MY_ATTRIBUTE((nonnull, warn_unused_result));
/** Drop a table.
If the data dictionary was not already locked by the transaction,
the transaction will be committed. Otherwise, the data dictionary
will remain locked.
@param[in] name Table name
@param[in,out] trx Transaction handle
@param[in] sqlcom type of SQL operation
@param[in] create_failed true=create table failed
because e.g. foreign key column
@param[in] nonatomic Whether it is permitted to release
and reacquire dict_sys.latch
@return error code */
dberr_t
row_drop_table_for_mysql(
const char* name,
trx_t* trx,
enum_sql_command sqlcom,
bool create_failed = false,
bool nonatomic = true);
/*********************************************************************//** /*********************************************************************//**
Discards the tablespace of a table which stored in an .ibd file. Discarding Discards the tablespace of a table which stored in an .ibd file. Discarding
means that this function deletes the .ibd file and assigns a new table id for means that this function deletes the .ibd file and assigns a new table id for
the table. Also the file_unreadable flag is set. the table. Also the file_unreadable flag is set.
@return error code or DB_SUCCESS */ @return error code or DB_SUCCESS */
dberr_t dberr_t row_discard_tablespace_for_mysql(dict_table_t *table, trx_t *trx)
row_discard_tablespace_for_mysql(
/*=============================*/
const char* name, /*!< in: table name */
trx_t* trx) /*!< in: transaction handle */
MY_ATTRIBUTE((nonnull, warn_unused_result)); MY_ATTRIBUTE((nonnull, warn_unused_result));
/*****************************************************************//** /*****************************************************************//**
Imports a tablespace. The space id in the .ibd file must match the space id Imports a tablespace. The space id in the .ibd file must match the space id
@@ -452,7 +412,6 @@ row_rename_table_for_mysql(
const char* old_name, /*!< in: old table name */ const char* old_name, /*!< in: old table name */
const char* new_name, /*!< in: new table name */ const char* new_name, /*!< in: new table name */
trx_t* trx, /*!< in/out: transaction */ trx_t* trx, /*!< in/out: transaction */
bool commit, /*!< in: whether to commit trx */
bool use_fk) /*!< in: whether to parse and enforce bool use_fk) /*!< in: whether to parse and enforce
FOREIGN KEY constraints */ FOREIGN KEY constraints */
MY_ATTRIBUTE((nonnull, warn_unused_result)); MY_ATTRIBUTE((nonnull, warn_unused_result));
@@ -472,17 +431,6 @@ row_scan_index_for_mysql(
ulint* n_rows) /*!< out: number of entries ulint* n_rows) /*!< out: number of entries
seen in the consistent read */ seen in the consistent read */
MY_ATTRIBUTE((warn_unused_result)); MY_ATTRIBUTE((warn_unused_result));
/*********************************************************************//**
Initialize this module */
void
row_mysql_init(void);
/*================*/
/*********************************************************************//**
Close this module */
void
row_mysql_close(void);
/*=================*/
/* A struct describing a place for an individual column in the MySQL /* A struct describing a place for an individual column in the MySQL
row format which is presented to the table handler in ha_innobase. row format which is presented to the table handler in ha_innobase.
@@ -946,10 +894,4 @@ innobase_rename_vc_templ(
#define ROW_READ_TRY_SEMI_CONSISTENT 1 #define ROW_READ_TRY_SEMI_CONSISTENT 1
#define ROW_READ_DID_SEMI_CONSISTENT 2 #define ROW_READ_DID_SEMI_CONSISTENT 2
#ifdef UNIV_DEBUG
/** Wait for the background drop list to become empty. */
void
row_wait_for_background_drop_list_empty();
#endif /* UNIV_DEBUG */
#endif /* row0mysql.h */ #endif /* row0mysql.h */

View File

@@ -377,7 +377,6 @@ enum monitor_id_t {
MONITOR_OVLD_SERVER_ACTIVITY, MONITOR_OVLD_SERVER_ACTIVITY,
MONITOR_MASTER_ACTIVE_LOOPS, MONITOR_MASTER_ACTIVE_LOOPS,
MONITOR_MASTER_IDLE_LOOPS, MONITOR_MASTER_IDLE_LOOPS,
MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND,
MONITOR_SRV_LOG_FLUSH_MICROSECOND, MONITOR_SRV_LOG_FLUSH_MICROSECOND,
MONITOR_SRV_DICT_LRU_MICROSECOND, MONITOR_SRV_DICT_LRU_MICROSECOND,
MONITOR_SRV_DICT_LRU_EVICT_COUNT_ACTIVE, MONITOR_SRV_DICT_LRU_EVICT_COUNT_ACTIVE,
@@ -400,7 +399,6 @@ enum monitor_id_t {
/* Data DDL related counters */ /* Data DDL related counters */
MONITOR_MODULE_DDL_STATS, MONITOR_MODULE_DDL_STATS,
MONITOR_BACKGROUND_DROP_INDEX, MONITOR_BACKGROUND_DROP_INDEX,
MONITOR_BACKGROUND_DROP_TABLE,
MONITOR_ONLINE_CREATE_INDEX, MONITOR_ONLINE_CREATE_INDEX,
MONITOR_PENDING_ALTER_TABLE, MONITOR_PENDING_ALTER_TABLE,
MONITOR_ALTER_TABLE_SORT_FILES, MONITOR_ALTER_TABLE_SORT_FILES,

View File

@@ -138,6 +138,8 @@ private:
Atomic_counter<uint32_t> m_paused; Atomic_counter<uint32_t> m_paused;
/** number of stop_SYS() calls without resume_SYS() */ /** number of stop_SYS() calls without resume_SYS() */
Atomic_counter<uint32_t> m_SYS_paused; Atomic_counter<uint32_t> m_SYS_paused;
/** number of stop_FTS() calls without resume_FTS() */
Atomic_counter<uint32_t> m_FTS_paused;
public: public:
que_t* query; /*!< The query graph which will do the que_t* query; /*!< The query graph which will do the
parallelized purge operation */ parallelized purge operation */
@@ -243,13 +245,14 @@ public:
/** @return whether the purge tasks are active */ /** @return whether the purge tasks are active */
bool running() const; bool running() const;
/** Stop purge during FLUSH TABLES FOR EXPORT */ /** Stop purge during FLUSH TABLES FOR EXPORT. */
void stop(); void stop();
/** Resume purge at UNLOCK TABLES after FLUSH TABLES FOR EXPORT */ /** Resume purge at UNLOCK TABLES after FLUSH TABLES FOR EXPORT */
void resume(); void resume();
private: private:
void wait_SYS(); void wait_SYS();
void wait_FTS();
public: public:
/** Suspend purge in data dictionary tables */ /** Suspend purge in data dictionary tables */
void stop_SYS(); void stop_SYS();
@@ -260,6 +263,15 @@ public:
/** check stop_SYS() */ /** check stop_SYS() */
void check_stop_SYS() { if (must_wait_SYS()) wait_SYS(); } void check_stop_SYS() { if (must_wait_SYS()) wait_SYS(); }
/** Pause purge during a DDL operation that could drop FTS_ tables. */
void stop_FTS() { m_FTS_paused++; }
/** Resume purge after stop_FTS(). */
void resume_FTS() { ut_d(const auto p=) m_FTS_paused--; ut_ad(p); }
/** @return whether stop_SYS() is in effect */
bool must_wait_FTS() const { return m_FTS_paused; }
/** check stop_SYS() */
void check_stop_FTS() { if (must_wait_FTS()) wait_FTS(); }
/** A wrapper around ReadView::changes_visible(). */ /** A wrapper around ReadView::changes_visible(). */
bool changes_visible(trx_id_t id, const table_name_t &name) const bool changes_visible(trx_id_t id, const table_name_t &name) const
{ {

View File

@@ -38,7 +38,6 @@ Created 3/26/1996 Heikki Tuuri
#include "ilist.h" #include "ilist.h"
#include <vector> #include <vector>
#include <set>
// Forward declaration // Forward declaration
struct mtr_t; struct mtr_t;
@@ -412,7 +411,8 @@ class trx_mod_table_time_t
/** First modification of the table, possibly ORed with BULK */ /** First modification of the table, possibly ORed with BULK */
undo_no_t first; undo_no_t first;
/** First modification of a system versioned column (or NONE) */ /** First modification of a system versioned column
(NONE= no versioning, BULK= the table was dropped) */
undo_no_t first_versioned= NONE; undo_no_t first_versioned= NONE;
public: public:
/** Constructor /** Constructor
@@ -427,17 +427,26 @@ public:
{ auto f= first & LIMIT; return f <= first_versioned && f <= rows; } { auto f= first & LIMIT; return f <= first_versioned && f <= rows; }
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
/** @return if versioned columns were modified */ /** @return if versioned columns were modified */
bool is_versioned() const { return first_versioned != NONE; } bool is_versioned() const { return (~first_versioned & LIMIT) != 0; }
/** @return if the table was dropped */
bool is_dropped() const { return first_versioned == BULK; }
/** After writing an undo log record, set is_versioned() if needed /** After writing an undo log record, set is_versioned() if needed
@param rows number of modified rows so far */ @param rows number of modified rows so far */
void set_versioned(undo_no_t rows) void set_versioned(undo_no_t rows)
{ {
ut_ad(!is_versioned()); ut_ad(first_versioned == NONE);
first_versioned= rows; first_versioned= rows;
ut_ad(valid(rows)); ut_ad(valid(rows));
} }
/** After writing an undo log record, note that the table will be dropped */
void set_dropped()
{
ut_ad(first_versioned == NONE);
first_versioned= BULK;
}
/** Notify the start of a bulk insert operation */ /** Notify the start of a bulk insert operation */
void start_bulk_insert() { first|= BULK; } void start_bulk_insert() { first|= BULK; }
@@ -923,6 +932,10 @@ private:
inline void commit_tables(); inline void commit_tables();
/** Mark a transaction committed in the main memory data structures. */ /** Mark a transaction committed in the main memory data structures. */
inline void commit_in_memory(const mtr_t *mtr); inline void commit_in_memory(const mtr_t *mtr);
/** Write log for committing the transaction. */
void commit_persist();
/** Clean up the transaction after commit_in_memory() */
void commit_cleanup();
/** Commit the transaction in a mini-transaction. /** Commit the transaction in a mini-transaction.
@param mtr mini-transaction (if there are any persistent modifications) */ @param mtr mini-transaction (if there are any persistent modifications) */
void commit_low(mtr_t *mtr= nullptr); void commit_low(mtr_t *mtr= nullptr);
@@ -931,6 +944,24 @@ public:
void commit(); void commit();
/** Try to drop a persistent table.
@param table persistent table
@param fk whether to drop FOREIGN KEY metadata
@return error code */
dberr_t drop_table(const dict_table_t &table);
/** Try to drop the foreign key constraints for a persistent table.
@param name name of persistent table
@return error code */
dberr_t drop_table_foreign(const table_name_t &name);
/** Try to drop the statistics for a persistent table.
@param name name of persistent table
@return error code */
dberr_t drop_table_statistics(const table_name_t &name);
/** Commit the transaction, possibly after drop_table().
@param deleted handles of data files that were deleted */
void commit(std::vector<pfs_os_file_t> &deleted);
/** Discard all savepoints */ /** Discard all savepoints */
void savepoints_discard() void savepoints_discard()
{ savepoints_discard(UT_LIST_GET_FIRST(trx_savepoints)); } { savepoints_discard(UT_LIST_GET_FIRST(trx_savepoints)); }

View File

@@ -1265,19 +1265,16 @@ lock_rec_enqueue_waiting(
trx_t* trx = thr_get_trx(thr); trx_t* trx = thr_get_trx(thr);
ut_ad(trx->mutex_is_owner()); ut_ad(trx->mutex_is_owner());
if (UNIV_UNLIKELY(trx->dict_operation)) { if (UNIV_UNLIKELY(trx->dict_operation_lock_mode == RW_X_LATCH)) {
ib::error() << "A record lock wait happens in a dictionary" ut_ad(!strcmp(index->table->name.m_name, TABLE_STATS_NAME)
" operation. index " || !strcmp(index->table->name.m_name, INDEX_STATS_NAME));
<< index->name instant_timeout:
<< " of table " trx->error_state = DB_LOCK_WAIT_TIMEOUT;
<< index->table->name return DB_LOCK_WAIT_TIMEOUT;
<< ". " << BUG_REPORT_MSG;
ut_ad(0);
} }
if (trx->mysql_thd && thd_lock_wait_timeout(trx->mysql_thd) == 0) { if (trx->mysql_thd && thd_lock_wait_timeout(trx->mysql_thd) == 0) {
trx->error_state = DB_LOCK_WAIT_TIMEOUT; goto instant_timeout;
return DB_LOCK_WAIT_TIMEOUT;
} }
/* Enqueue the lock request that will wait to be granted, note that /* Enqueue the lock request that will wait to be granted, note that
@@ -3314,11 +3311,11 @@ lock_table_enqueue_waiting(
trx_t* trx = thr_get_trx(thr); trx_t* trx = thr_get_trx(thr);
ut_ad(trx->mutex_is_owner()); ut_ad(trx->mutex_is_owner());
if (UNIV_UNLIKELY(trx->dict_operation)) { if (UNIV_UNLIKELY(trx->dict_operation_lock_mode == RW_X_LATCH)) {
ib::error() << "A table lock wait happens in a dictionary" ut_ad(!strcmp(table->name.m_name, TABLE_STATS_NAME)
" operation. Table " << table->name || !strcmp(table->name.m_name, INDEX_STATS_NAME));
<< ". " << BUG_REPORT_MSG; trx->error_state = DB_LOCK_WAIT_TIMEOUT;
ut_ad(0); return DB_LOCK_WAIT_TIMEOUT;
} }
#ifdef WITH_WSREP #ifdef WITH_WSREP

View File

@@ -1482,8 +1482,7 @@ os_file_rename_func(
/* New path must not exist. */ /* New path must not exist. */
ut_ad(os_file_status(newpath, &exists, &type)); ut_ad(os_file_status(newpath, &exists, &type));
/* MDEV-25506 FIXME: Remove the strstr() */ ut_ad(!exists);
ut_ad(!exists || strstr(oldpath, "/" TEMP_FILE_PREFIX_INNODB));
/* Old path must exist. */ /* Old path must exist. */
ut_ad(os_file_status(oldpath, &exists, &type)); ut_ad(os_file_status(oldpath, &exists, &type));
@@ -2535,8 +2534,7 @@ os_file_rename_func(
/* New path must not exist. */ /* New path must not exist. */
ut_ad(os_file_status(newpath, &exists, &type)); ut_ad(os_file_status(newpath, &exists, &type));
/* MDEV-25506 FIXME: Remove the strstr() */ ut_ad(!exists);
ut_ad(!exists || strstr(oldpath, "/" TEMP_FILE_PREFIX_INNODB));
/* Old path must exist. */ /* Old path must exist. */
ut_ad(os_file_status(oldpath, &exists, &type)); ut_ad(os_file_status(oldpath, &exists, &type));

View File

@@ -1830,16 +1830,12 @@ do_possible_lock_wait:
thr->lock_state = QUE_THR_LOCK_ROW; thr->lock_state = QUE_THR_LOCK_ROW;
check_table->inc_fk_checks();
err = lock_wait(thr); err = lock_wait(thr);
thr->lock_state = QUE_THR_LOCK_NOLOCK; thr->lock_state = QUE_THR_LOCK_NOLOCK;
check_table->dec_fk_checks();
if (err != DB_SUCCESS) { if (err != DB_SUCCESS) {
} else if (check_table->to_be_dropped) { } else if (check_table->name.is_temporary()) {
err = DB_LOCK_WAIT_TIMEOUT; err = DB_LOCK_WAIT_TIMEOUT;
} else { } else {
err = DB_LOCK_WAIT; err = DB_LOCK_WAIT;
@@ -1912,14 +1908,10 @@ row_ins_check_foreign_constraints(
{ {
dict_foreign_t* foreign; dict_foreign_t* foreign;
dberr_t err = DB_SUCCESS; dberr_t err = DB_SUCCESS;
trx_t* trx;
ibool got_s_lock = FALSE;
mem_heap_t* heap = NULL; mem_heap_t* heap = NULL;
DBUG_ASSERT(index->is_primary() == pk); DBUG_ASSERT(index->is_primary() == pk);
trx = thr_get_trx(thr);
DEBUG_SYNC_C_IF_THD(thr_get_trx(thr)->mysql_thd, DEBUG_SYNC_C_IF_THD(thr_get_trx(thr)->mysql_thd,
"foreign_constraint_check_for_ins"); "foreign_constraint_check_for_ins");
@@ -1965,32 +1957,9 @@ row_ins_check_foreign_constraints(
FALSE, FALSE, DICT_ERR_IGNORE_NONE); FALSE, FALSE, DICT_ERR_IGNORE_NONE);
} }
if (0 == trx->dict_operation_lock_mode) {
got_s_lock = TRUE;
row_mysql_freeze_data_dictionary(trx);
}
if (referenced_table) {
foreign->foreign_table->inc_fk_checks();
}
/* NOTE that if the thread ends up waiting for a lock
we will release dict_sys.latch temporarily!
But the counter on the table protects the referenced
table from being dropped while the check is running. */
err = row_ins_check_foreign_constraint( err = row_ins_check_foreign_constraint(
TRUE, foreign, table, ref_tuple, thr); TRUE, foreign, table, ref_tuple, thr);
if (referenced_table) {
foreign->foreign_table->dec_fk_checks();
}
if (got_s_lock) {
row_mysql_unfreeze_data_dictionary(trx);
}
if (ref_table != NULL) { if (ref_table != NULL) {
dict_table_close(ref_table, FALSE, FALSE); dict_table_close(ref_table, FALSE, FALSE);
} }

View File

@@ -3787,7 +3787,7 @@ static void row_merge_drop_fulltext_indexes(trx_t *trx, dict_table_t *table)
return; return;
fts_optimize_remove_table(table); fts_optimize_remove_table(table);
fts_drop_tables(trx, table); fts_drop_tables(trx, *table);
fts_free(table); fts_free(table);
DICT_TF2_FLAG_UNSET(table, DICT_TF2_FTS); DICT_TF2_FLAG_UNSET(table, DICT_TF2_FTS);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -111,8 +111,10 @@ row_purge_remove_clust_if_poss_low(
MDL_ticket* mdl_ticket = nullptr; MDL_ticket* mdl_ticket = nullptr;
dict_table_t *table = nullptr; dict_table_t *table = nullptr;
pfs_os_file_t f = OS_FILE_CLOSED; pfs_os_file_t f = OS_FILE_CLOSED;
retry:
if (table_id) { if (table_id) {
retry:
purge_sys.check_stop_FTS();
dict_sys.mutex_lock(); dict_sys.mutex_lock();
table = dict_table_open_on_id( table = dict_table_open_on_id(
table_id, true, DICT_TABLE_OP_OPEN_ONLY_IF_CACHED, table_id, true, DICT_TABLE_OP_OPEN_ONLY_IF_CACHED,
@@ -184,7 +186,7 @@ close_and_exit:
mdl_ticket = nullptr; mdl_ticket = nullptr;
} }
} }
fil_delete_tablespace(space_id, true, &f); f = fil_delete_tablespace(space_id);
} }
mtr.commit(); mtr.commit();
@@ -739,6 +741,12 @@ void purge_sys_t::wait_SYS()
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
} }
void purge_sys_t::wait_FTS()
{
while (must_wait_FTS())
std::this_thread::sleep_for(std::chrono::seconds(1));
}
/** Reset DB_TRX_ID, DB_ROLL_PTR of a clustered index record /** Reset DB_TRX_ID, DB_ROLL_PTR of a clustered index record
whose old history can no longer be observed. whose old history can no longer be observed.
@param[in,out] node purge node @param[in,out] node purge node
@@ -1034,11 +1042,13 @@ row_purge_parse_undo_rec(
} }
try_again: try_again:
purge_sys.check_stop_FTS();
node->table = dict_table_open_on_id( node->table = dict_table_open_on_id(
table_id, false, DICT_TABLE_OP_NORMAL, node->purge_thd, table_id, false, DICT_TABLE_OP_NORMAL, node->purge_thd,
&node->mdl_ticket); &node->mdl_ticket);
if (node->table == NULL || node->table->name.is_temporary()) { if (!node->table) {
/* The table has been dropped: no need to do purge and /* The table has been dropped: no need to do purge and
release mdl happened as a part of open process itself */ release mdl happened as a part of open process itself */
goto err_exit; goto err_exit;

View File

@@ -140,6 +140,27 @@ restart:
mem_heap_free(heap); mem_heap_free(heap);
} else { } else {
switch (node->table->id) { switch (node->table->id) {
case DICT_COLUMNS_ID:
/* This is rolling back an INSERT into SYS_COLUMNS.
If it was part of an instant ALTER TABLE operation, we
must evict the table definition, so that it can be
reloaded after the dictionary operation has been
completed. At this point, any corresponding operation
to the metadata record will have been rolled back. */
ut_ad(!online);
ut_ad(node->trx->dict_operation_lock_mode
== RW_X_LATCH);
ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
if (rec_get_n_fields_old(rec)
!= DICT_NUM_FIELDS__SYS_COLUMNS
|| (rec_get_1byte_offs_flag(rec)
? rec_1_get_field_end_info(rec, 0) != 8
: rec_2_get_field_end_info(rec, 0) != 8)) {
break;
}
static_assert(!DICT_FLD__SYS_COLUMNS__TABLE_ID, "");
node->trx->evict_table(mach_read_from_8(rec));
break;
case DICT_INDEXES_ID: case DICT_INDEXES_ID:
ut_ad(!online); ut_ad(!online);
ut_ad(node->trx->dict_operation_lock_mode ut_ad(node->trx->dict_operation_lock_mode
@@ -154,6 +175,8 @@ restart:
ut_ad("corrupted SYS_INDEXES record" == 0); ut_ad("corrupted SYS_INDEXES record" == 0);
} }
pfs_os_file_t d = OS_FILE_CLOSED;
if (const uint32_t space_id = dict_drop_index_tree( if (const uint32_t space_id = dict_drop_index_tree(
&node->pcur, node->trx, &mtr)) { &node->pcur, node->trx, &mtr)) {
if (table) { if (table) {
@@ -185,36 +208,19 @@ restart:
} }
} }
fil_delete_tablespace(space_id, true); d = fil_delete_tablespace(space_id);
} }
mtr.commit(); mtr.commit();
if (d != OS_FILE_CLOSED) {
os_file_close(d);
}
mtr.start(); mtr.start();
success = btr_pcur_restore_position( success = btr_pcur_restore_position(
BTR_MODIFY_LEAF, &node->pcur, &mtr); BTR_MODIFY_LEAF, &node->pcur, &mtr);
ut_a(success); ut_a(success);
break;
case DICT_COLUMNS_ID:
/* This is rolling back an INSERT into SYS_COLUMNS.
If it was part of an instant ALTER TABLE operation, we
must evict the table definition, so that it can be
reloaded after the dictionary operation has been
completed. At this point, any corresponding operation
to the metadata record will have been rolled back. */
ut_ad(!online);
ut_ad(node->trx->dict_operation_lock_mode
== RW_X_LATCH);
ut_ad(node->rec_type == TRX_UNDO_INSERT_REC);
if (rec_get_n_fields_old(rec)
!= DICT_NUM_FIELDS__SYS_COLUMNS
|| (rec_get_1byte_offs_flag(rec)
? rec_1_get_field_end_info(rec, 0) != 8
: rec_2_get_field_end_info(rec, 0) != 8)) {
break;
}
static_assert(!DICT_FLD__SYS_COLUMNS__TABLE_ID, "");
node->trx->evict_table(mach_read_from_8(rec));
} }
} }

View File

@@ -281,21 +281,9 @@ row_upd_check_references_constraints(
FALSE, FALSE, DICT_ERR_IGNORE_NONE); FALSE, FALSE, DICT_ERR_IGNORE_NONE);
} }
if (foreign_table) {
foreign_table->inc_fk_checks();
}
/* NOTE that if the thread ends up waiting for a lock
we will release dict_sys.latch temporarily!
But the inc_fk_checks() protects foreign_table from
being dropped while the check is running. */
err = row_ins_check_foreign_constraint( err = row_ins_check_foreign_constraint(
FALSE, foreign, table, entry, thr); FALSE, foreign, table, entry, thr);
if (foreign_table) {
foreign_table->dec_fk_checks();
}
if (ref_table != NULL) { if (ref_table != NULL) {
dict_table_close(ref_table, FALSE, FALSE); dict_table_close(ref_table, FALSE, FALSE);
} }

View File

@@ -1090,11 +1090,6 @@ static monitor_info_t innodb_counter_info[] =
MONITOR_NONE, MONITOR_NONE,
MONITOR_DEFAULT_START, MONITOR_MASTER_IDLE_LOOPS}, MONITOR_DEFAULT_START, MONITOR_MASTER_IDLE_LOOPS},
{"innodb_background_drop_table_usec", "server",
"Time (in microseconds) spent to process drop table list",
MONITOR_NONE,
MONITOR_DEFAULT_START, MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND},
{"innodb_log_flush_usec", "server", {"innodb_log_flush_usec", "server",
"Time (in microseconds) spent to flush log records", "Time (in microseconds) spent to flush log records",
MONITOR_NONE, MONITOR_NONE,
@@ -1189,11 +1184,6 @@ static monitor_info_t innodb_counter_info[] =
MONITOR_NONE, MONITOR_NONE,
MONITOR_DEFAULT_START, MONITOR_BACKGROUND_DROP_INDEX}, MONITOR_DEFAULT_START, MONITOR_BACKGROUND_DROP_INDEX},
{"ddl_background_drop_tables", "ddl",
"Number of tables in background drop table list",
MONITOR_NONE,
MONITOR_DEFAULT_START, MONITOR_BACKGROUND_DROP_TABLE},
{"ddl_online_create_index", "ddl", {"ddl_online_create_index", "ddl",
"Number of indexes being created online", "Number of indexes being created online",
MONITOR_NONE, MONITOR_NONE,

View File

@@ -677,7 +677,6 @@ srv_boot(void)
{ {
srv_thread_pool_init(); srv_thread_pool_init();
trx_pool_init(); trx_pool_init();
row_mysql_init();
srv_init(); srv_init();
} }
@@ -1374,6 +1373,8 @@ void purge_sys_t::stop_SYS()
/** Stop purge during FLUSH TABLES FOR EXPORT */ /** Stop purge during FLUSH TABLES FOR EXPORT */
void purge_sys_t::stop() void purge_sys_t::stop()
{ {
dict_sys.assert_not_locked();
for (;;) for (;;)
{ {
latch.wr_lock(SRW_LOCK_CALL); latch.wr_lock(SRW_LOCK_CALL);
@@ -1471,10 +1472,7 @@ The master thread is tasked to ensure that flush of log file happens
once every second in the background. This is to ensure that not more once every second in the background. This is to ensure that not more
than one second of trxs are lost in case of crash when than one second of trxs are lost in case of crash when
innodb_flush_logs_at_trx_commit != 1 */ innodb_flush_logs_at_trx_commit != 1 */
static static void srv_sync_log_buffer_in_background()
void
srv_sync_log_buffer_in_background(void)
/*===================================*/
{ {
time_t current_time = time(NULL); time_t current_time = time(NULL);
@@ -1496,8 +1494,6 @@ srv_shutdown_print_master_pending(
/*==============================*/ /*==============================*/
time_t* last_print_time, /*!< last time the function time_t* last_print_time, /*!< last time the function
print the message */ print the message */
ulint n_tables_to_drop, /*!< number of tables to
be dropped */
ulint n_bytes_merged) /*!< number of change buffer ulint n_bytes_merged) /*!< number of change buffer
just merged */ just merged */
{ {
@@ -1506,11 +1502,6 @@ srv_shutdown_print_master_pending(
if (difftime(current_time, *last_print_time) > 60) { if (difftime(current_time, *last_print_time) > 60) {
*last_print_time = current_time; *last_print_time = current_time;
if (n_tables_to_drop) {
ib::info() << "Waiting for " << n_tables_to_drop
<< " table(s) to be dropped";
}
/* Check change buffer merge, we only wait for change buffer /* Check change buffer merge, we only wait for change buffer
merge if it is a slow shutdown */ merge if it is a slow shutdown */
if (!srv_fast_shutdown && n_bytes_merged) { if (!srv_fast_shutdown && n_bytes_merged) {
@@ -1585,14 +1576,6 @@ srv_master_do_active_tasks(void)
MONITOR_INC(MONITOR_MASTER_ACTIVE_LOOPS); MONITOR_INC(MONITOR_MASTER_ACTIVE_LOOPS);
/* ALTER TABLE in MySQL requires on Unix that the table handler
can drop tables lazily after there no longer are SELECT
queries to them. */
srv_main_thread_op_info = "doing background drop tables";
row_drop_tables_for_mysql_in_background();
MONITOR_INC_TIME_IN_MICRO_SECS(
MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND, counter_time);
ut_d(srv_master_do_disabled_loop()); ut_d(srv_master_do_disabled_loop());
if (srv_shutdown_state > SRV_SHUTDOWN_INITIATED) { if (srv_shutdown_state > SRV_SHUTDOWN_INITIATED) {
@@ -1646,17 +1629,6 @@ srv_master_do_idle_tasks(void)
MONITOR_INC(MONITOR_MASTER_IDLE_LOOPS); MONITOR_INC(MONITOR_MASTER_IDLE_LOOPS);
/* ALTER TABLE in MySQL requires on Unix that the table handler
can drop tables lazily after there no longer are SELECT
queries to them. */
ulonglong counter_time = microsecond_interval_timer();
srv_main_thread_op_info = "doing background drop tables";
row_drop_tables_for_mysql_in_background();
MONITOR_INC_TIME_IN_MICRO_SECS(
MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND,
counter_time);
ut_d(srv_master_do_disabled_loop()); ut_d(srv_master_do_disabled_loop());
if (srv_shutdown_state > SRV_SHUTDOWN_INITIATED) { if (srv_shutdown_state > SRV_SHUTDOWN_INITIATED) {
@@ -1672,6 +1644,7 @@ srv_master_do_idle_tasks(void)
return; return;
} }
ulonglong counter_time = microsecond_interval_timer();
srv_main_thread_op_info = "enforcing dict cache limit"; srv_main_thread_op_info = "enforcing dict cache limit";
if (ulint n_evicted = dict_sys.evict_table_LRU(false)) { if (ulint n_evicted = dict_sys.evict_table_LRU(false)) {
MONITOR_INC_VALUE( MONITOR_INC_VALUE(
@@ -1692,7 +1665,6 @@ and optionally change buffer merge (on innodb_fast_shutdown=0). */
void srv_shutdown(bool ibuf_merge) void srv_shutdown(bool ibuf_merge)
{ {
ulint n_bytes_merged = 0; ulint n_bytes_merged = 0;
ulint n_tables_to_drop;
time_t now = time(NULL); time_t now = time(NULL);
do { do {
@@ -1700,11 +1672,6 @@ void srv_shutdown(bool ibuf_merge)
ut_ad(srv_shutdown_state == SRV_SHUTDOWN_CLEANUP); ut_ad(srv_shutdown_state == SRV_SHUTDOWN_CLEANUP);
++srv_main_shutdown_loops; ++srv_main_shutdown_loops;
/* FIXME: Remove the background DROP TABLE queue; it is not
crash-safe and breaks ACID. */
srv_main_thread_op_info = "doing background drop tables";
n_tables_to_drop = row_drop_tables_for_mysql_in_background();
if (ibuf_merge) { if (ibuf_merge) {
srv_main_thread_op_info = "checking free log space"; srv_main_thread_op_info = "checking free log space";
log_free_check(); log_free_check();
@@ -1717,10 +1684,10 @@ void srv_shutdown(bool ibuf_merge)
/* Print progress message every 60 seconds during shutdown */ /* Print progress message every 60 seconds during shutdown */
if (srv_print_verbose_log) { if (srv_print_verbose_log) {
srv_shutdown_print_master_pending( srv_shutdown_print_master_pending(&now,
&now, n_tables_to_drop, n_bytes_merged); n_bytes_merged);
} }
} while (n_bytes_merged || n_tables_to_drop); } while (n_bytes_merged);
} }
/** The periodic master task controlling the server. */ /** The periodic master task controlling the server. */

View File

@@ -1764,9 +1764,6 @@ file_checked:
&& !srv_read_only_mode) { && !srv_read_only_mode) {
/* Drop partially created indexes. */ /* Drop partially created indexes. */
row_merge_drop_temp_indexes(); row_merge_drop_temp_indexes();
/* Drop garbage tables. */
row_mysql_drop_garbage_tables();
/* Rollback incomplete non-DDL transactions */ /* Rollback incomplete non-DDL transactions */
trx_rollback_is_active = true; trx_rollback_is_active = true;
srv_thread_pool->submit_task(&rollback_all_recovered_task); srv_thread_pool->submit_task(&rollback_all_recovered_task);
@@ -1898,10 +1895,6 @@ void srv_shutdown_bg_undo_sources()
ut_ad(!srv_read_only_mode); ut_ad(!srv_read_only_mode);
fts_optimize_shutdown(); fts_optimize_shutdown();
dict_stats_shutdown(); dict_stats_shutdown();
while (row_get_background_drop_list_len_low()) {
srv_inc_activity_count();
std::this_thread::yield();
}
srv_undo_sources = false; srv_undo_sources = false;
} }
} }
@@ -2028,7 +2021,6 @@ void innodb_shutdown()
dict_sys.close(); dict_sys.close();
btr_search_sys_free(); btr_search_sys_free();
row_mysql_close();
srv_free(); srv_free();
fil_system.close(); fil_system.close();
pars_lexer_close(); pars_lexer_close();

View File

@@ -694,7 +694,8 @@ not_free:
/* Adjust the tablespace metadata. */ /* Adjust the tablespace metadata. */
mysql_mutex_lock(&fil_system.mutex); mysql_mutex_lock(&fil_system.mutex);
space.set_stopping(true); ut_d(bool stopped=) space.set_stopping();
ut_ad(!stopped);
space.is_being_truncated = true; space.is_being_truncated = true;
if (space.crypt_data) { if (space.crypt_data) {
space.reacquire(); space.reacquire();
@@ -806,7 +807,7 @@ not_free:
mysql_mutex_lock(&fil_system.mutex); mysql_mutex_lock(&fil_system.mutex);
ut_ad(&space == purge_sys.truncate.current); ut_ad(&space == purge_sys.truncate.current);
ut_ad(space.is_being_truncated); ut_ad(space.is_being_truncated);
purge_sys.truncate.current->set_stopping(false); purge_sys.truncate.current->clear_stopping();
purge_sys.truncate.current->is_being_truncated = false; purge_sys.truncate.current->is_being_truncated = false;
mysql_mutex_unlock(&fil_system.mutex); mysql_mutex_unlock(&fil_system.mutex);

View File

@@ -59,6 +59,8 @@ const trx_t* trx_roll_crash_recv_trx;
@retval false if the rollback was aborted by shutdown */ @retval false if the rollback was aborted by shutdown */
inline bool trx_t::rollback_finish() inline bool trx_t::rollback_finish()
{ {
mod_tables.clear();
if (UNIV_LIKELY(error_state == DB_SUCCESS)) if (UNIV_LIKELY(error_state == DB_SUCCESS))
{ {
commit(); commit();
@@ -89,6 +91,7 @@ inline bool trx_t::rollback_finish()
undo= nullptr; undo= nullptr;
} }
commit_low(); commit_low();
commit_cleanup();
return false; return false;
} }

View File

@@ -1427,6 +1427,10 @@ inline void trx_t::commit_in_memory(const mtr_t *mtr)
ut_ad(!(lock.was_chosen_as_deadlock_victim & byte(~2U))); ut_ad(!(lock.was_chosen_as_deadlock_victim & byte(~2U)));
lock.was_chosen_as_deadlock_victim= false; lock.was_chosen_as_deadlock_victim= false;
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
}
void trx_t::commit_cleanup()
{
mutex.wr_lock(); mutex.wr_lock();
dict_operation= false; dict_operation= false;
@@ -1452,16 +1456,19 @@ void trx_t::commit_low(mtr_t *mtr)
ut_ad(!mtr == (aborted || !has_logged_or_recovered())); ut_ad(!mtr == (aborted || !has_logged_or_recovered()));
ut_ad(!mtr || !aborted); ut_ad(!mtr || !aborted);
/* undo_no is non-zero if we're doing the final commit. */
if (fts_trx && undo_no) if (fts_trx && undo_no)
{ {
ut_a(!is_autocommit_non_locking()); ut_a(!is_autocommit_non_locking());
/* FTS-FIXME: Temporarily tolerate DB_DUPLICATE_KEY instead of /* MDEV-24088 FIXME: Invoke fts_commit() earlier (before possible
dying. This is a possible scenario if there is a crash between XA PREPARE), so that we will be able to return an error and rollback
the transaction, instead of violating consistency!
The original claim about DB_DUPLICATE KEY was:
This is a possible scenario if there is a crash between
insert to DELETED table committing and transaction committing. The insert to DELETED table committing and transaction committing. The
fix would be able to return error from this function */ fix would be able to return error from this function */
if (dberr_t error= fts_commit(this)) if (ut_d(dberr_t error=) fts_commit(this))
ut_a(error == DB_DUPLICATE_KEY); ut_ad(error == DB_DUPLICATE_KEY || error == DB_LOCK_WAIT_TIMEOUT);
} }
#ifndef DBUG_OFF #ifndef DBUG_OFF
@@ -1498,7 +1505,7 @@ void trx_t::commit_low(mtr_t *mtr)
} }
void trx_t::commit() void trx_t::commit_persist()
{ {
mtr_t *mtr= nullptr; mtr_t *mtr= nullptr;
mtr_t local_mtr; mtr_t local_mtr;
@@ -1511,6 +1518,15 @@ void trx_t::commit()
commit_low(mtr); commit_low(mtr);
} }
void trx_t::commit()
{
commit_persist();
ut_d(for (const auto &p : mod_tables) ut_ad(!p.second.is_dropped()));
commit_cleanup();
}
/****************************************************************//** /****************************************************************//**
Prepares a transaction for commit/rollback. */ Prepares a transaction for commit/rollback. */
void void

View File

@@ -428,8 +428,6 @@ ut_strerr(
return("End of index"); return("End of index");
case DB_IO_ERROR: case DB_IO_ERROR:
return("I/O error"); return("I/O error");
case DB_TABLE_IN_FK_CHECK:
return("Table is being used in foreign key check");
case DB_NOT_FOUND: case DB_NOT_FOUND:
return("not found"); return("not found");
case DB_ONLINE_LOG_TOO_BIG: case DB_ONLINE_LOG_TOO_BIG:

View File

@@ -210,7 +210,6 @@ innodb_master_thread_sleeps server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL N
innodb_activity_count server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Current server activity count innodb_activity_count server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Current server activity count
innodb_master_active_loops server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of times master thread performs its tasks when server is active innodb_master_active_loops server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of times master thread performs its tasks when server is active
innodb_master_idle_loops server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of times master thread performs its tasks when server is idle innodb_master_idle_loops server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of times master thread performs its tasks when server is idle
innodb_background_drop_table_usec server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Time (in microseconds) spent to process drop table list
innodb_log_flush_usec server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Time (in microseconds) spent to flush log records innodb_log_flush_usec server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Time (in microseconds) spent to flush log records
innodb_dict_lru_usec server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Time (in microseconds) spent to process DICT LRU list innodb_dict_lru_usec server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Time (in microseconds) spent to process DICT LRU list
innodb_dict_lru_count_active server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of tables evicted from DICT LRU list in the active loop innodb_dict_lru_count_active server 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of tables evicted from DICT LRU list in the active loop
@@ -227,7 +226,6 @@ dml_system_inserts dml 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 s
dml_system_deletes dml 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Number of system rows deleted dml_system_deletes dml 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Number of system rows deleted
dml_system_updates dml 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Number of system rows updated dml_system_updates dml 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 status_counter Number of system rows updated
ddl_background_drop_indexes ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of indexes waiting to be dropped after failed index creation ddl_background_drop_indexes ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of indexes waiting to be dropped after failed index creation
ddl_background_drop_tables ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of tables in background drop table list
ddl_online_create_index ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of indexes being created online ddl_online_create_index ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of indexes being created online
ddl_pending_alter_table ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of ALTER TABLE, CREATE INDEX, DROP INDEX in progress ddl_pending_alter_table ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of ALTER TABLE, CREATE INDEX, DROP INDEX in progress
ddl_sort_file_alter_table ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of sort files created during alter table ddl_sort_file_alter_table ddl 0 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL 0 counter Number of sort files created during alter table