diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
commit | 3f619478f796eddbba6e39502fe941b285dd97b1 (patch) | |
tree | e2c7b5777f728320e5b5542b6213fd3591ba51e2 /mysql-test/suite/innodb/t/innodb-index-online.test | |
parent | Initial commit. (diff) | |
download | mariadb-upstream.tar.xz mariadb-upstream.zip |
Adding upstream version 1:10.11.6.upstream/1%10.11.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mysql-test/suite/innodb/t/innodb-index-online.test')
-rw-r--r-- | mysql-test/suite/innodb/t/innodb-index-online.test | 524 |
1 files changed, 524 insertions, 0 deletions
diff --git a/mysql-test/suite/innodb/t/innodb-index-online.test b/mysql-test/suite/innodb/t/innodb-index-online.test new file mode 100644 index 00000000..f8eb8957 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb-index-online.test @@ -0,0 +1,524 @@ +--source include/innodb_page_size_small.inc +--source include/innodb_encrypt_log.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc +--source include/no_valgrind_without_big.inc + +let $innodb_metrics_select= +SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem = 'ddl'; + +call mtr.add_suppression("InnoDB: Warning: Small buffer pool size"); + +# DISCARD TABLESPACE needs file-per-table +SET @global_innodb_file_per_table_orig = @@global.innodb_file_per_table; +SET GLOBAL innodb_file_per_table = on; + +# Save the initial number of concurrent sessions. +--source include/count_sessions.inc + +CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT, c3 TEXT) +ENGINE=InnoDB STATS_PERSISTENT=0; +INSERT INTO t1 VALUES (1,1,''), (2,2,''), (3,3,''), (4,4,''), (5,5,''); + +SET GLOBAL innodb_monitor_enable = module_ddl; +eval $innodb_metrics_select; + +SET DEBUG_SYNC = 'RESET'; +SET DEBUG_SYNC = 'write_row_noreplace SIGNAL have_handle WAIT_FOR go_ahead'; +--send +INSERT INTO t1 VALUES(1,2,3); + +connect (con1,localhost,root,,); + +# This should block at the end because of the INSERT in connection default +# is holding a metadata lock. +SET DEBUG_SYNC = 'now WAIT_FOR have_handle'; +SET lock_wait_timeout = 1; +--error ER_LOCK_WAIT_TIMEOUT +ALTER TABLE t1 ADD UNIQUE INDEX(c2); +SET DEBUG_SYNC = 'now SIGNAL go_ahead'; + +connection default; +--error ER_DUP_ENTRY +reap; +eval $innodb_metrics_select; + +connection con1; +SET @saved_debug_dbug = @@SESSION.debug_dbug; +SET DEBUG_DBUG = '+d,innodb_OOM_prepare_inplace_alter'; +--error ER_OUT_OF_RESOURCES +ALTER TABLE t1 ADD UNIQUE INDEX(c2); +SET DEBUG_DBUG = @saved_debug_dbug; +SET DEBUG_DBUG = '+d,innodb_OOM_inplace_alter'; +--error ER_OUT_OF_RESOURCES +CREATE UNIQUE INDEX c2 ON t1(c2); +SET DEBUG_DBUG = @saved_debug_dbug; +SET DEBUG_DBUG = '+d,innodb_OOM_prepare_add_index'; +--error ER_OUT_OF_RESOURCES +ALTER TABLE t1 ADD KEY(c2), ADD KEY c3_10(c3(10)), ADD KEY c3_c2(c3(4),c2); +SET DEBUG_DBUG = @saved_debug_dbug; +CREATE UNIQUE INDEX c2 ON t1(c2); +DROP INDEX c2 ON t1; + +connection default; +SHOW CREATE TABLE t1; +# Insert a duplicate entry (4) for the upcoming UNIQUE INDEX(c2). +BEGIN; +INSERT INTO t1 VALUES(7,4,2); + +connection con1; +# This DEBUG_SYNC should not kick in yet, because the duplicate key will be +# detected before we get a chance to apply the online log. +SET DEBUG_SYNC = 'row_log_apply_before SIGNAL scanned WAIT_FOR rollback_done'; +# This will be a lock wait timeout on the meta-data lock, +# because the transaction inserting (7,4,2) is still active. +--error ER_LOCK_WAIT_TIMEOUT +ALTER TABLE t1 ADD UNIQUE INDEX(c2); +connection default; +COMMIT; +connection con1; +--error ER_DUP_ENTRY +ALTER TABLE t1 ADD UNIQUE INDEX(c2); +connection default; +DELETE FROM t1 WHERE c1 = 7; +connection con1; +# ADD FOREIGN KEY is not supported in-place +--error ER_ALTER_OPERATION_NOT_SUPPORTED_REASON +ALTER TABLE t1 ADD FOREIGN KEY(c2) REFERENCES t1(c2), ALGORITHM = INPLACE; +# The previous DEBUG_SYNC should be ignored, because an exclusive lock +# has been requested and the online log is not being allocated. +ALTER TABLE t1 ADD UNIQUE INDEX(c2), LOCK = EXCLUSIVE, ALGORITHM = INPLACE; +DROP INDEX c2 ON t1; +# Now the previous DEBUG_SYNC should kick in. +--send +ALTER TABLE t1 ADD UNIQUE INDEX(c2); +connection default; +SET DEBUG_SYNC = 'now WAIT_FOR scanned'; +eval $innodb_metrics_select; + +# Insert a duplicate entry (4) for the already started UNIQUE INDEX(c2). +BEGIN; +INSERT INTO t1 VALUES(7,4,2); +COMMIT; +DELETE FROM t1 where c1 = 7; +SET DEBUG_SYNC = 'now SIGNAL rollback_done'; + +connection con1; +# Because the modification log will be applied in order, there will be +# a duplicate key error on the (7,4,2) even though we roll it back. +--error ER_DUP_ENTRY +reap; +# Now, create the index without any concurrent DML, while no duplicate exists. +SET DEBUG_SYNC = 'row_log_apply_after SIGNAL created WAIT_FOR dml_done'; +--send +ALTER TABLE t1 ADD UNIQUE INDEX(c2); +connection default; +SET DEBUG_SYNC = 'now WAIT_FOR created'; +# At this point, the index has been created inside InnoDB but not yet +# in the MySQL data dictionary. +eval $innodb_metrics_select; +# A duplicate key error should now be triggered by InnoDB, but reported +# by the ALTER TABLE because the index does not 'officially' exist yet. +INSERT INTO t1 VALUES(6,3,1); +SET DEBUG_SYNC = 'now SIGNAL dml_done'; +connection con1; +# This is due to the duplicate entry (6,3,1). +--error ER_DUP_ENTRY +reap; +DELETE FROM t1 WHERE c1=6; +ALTER TABLE t1 ADD UNIQUE INDEX(c2); +eval $innodb_metrics_select; + +connection default; +--error ER_DUP_ENTRY +INSERT INTO t1 VALUES(6,3,1); +--error ER_DUP_ENTRY +INSERT INTO t1 VALUES(7,4,2); +ALTER TABLE t1 STATS_PERSISTENT=1; +ANALYZE TABLE t1; +# Purge may or may not have cleaned up the DELETE FROM t1 WHERE c1 = 7; +UPDATE mysql.innodb_index_stats SET stat_value = 5 +WHERE database_name = 'test' AND table_name= 't1' AND index_name = 'PRIMARY' +AND stat_value = 6; +--replace_column 4 LAST_UPDATE +SELECT * FROM mysql.innodb_index_stats WHERE table_name IN ('t1'); +CREATE TABLE t1_c2_stats SELECT * FROM mysql.innodb_index_stats +WHERE database_name = 'test' AND table_name = 't1' and index_name = 'c2'; +# in Embedded mode (./mtr --embedded-server) the t1_c2_stats table gets +# created in MyISAM format by default even if we set +# default_storage_engine='innodb' +ALTER TABLE t1_c2_stats ENGINE=INNODB; +DROP INDEX c2 ON t1; +ANALYZE TABLE t1_c2_stats; +--replace_column 4 LAST_UPDATE +SELECT * FROM mysql.innodb_index_stats WHERE table_name IN ('t1', 't1_c2_stats'); + +connection con1; +let $ID= `SELECT @id := CONNECTION_ID()`; +--error ER_QUERY_INTERRUPTED +KILL QUERY @id; + +SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2d_created WAIT_FOR kill_done'; +--send +CREATE INDEX c2d ON t1(c2); + +connection default; +SET DEBUG_SYNC = 'now WAIT_FOR c2d_created'; +eval $innodb_metrics_select; +let $ignore= `SELECT @id := $ID`; +KILL QUERY @id; +SET DEBUG_SYNC = 'now SIGNAL kill_done'; + +connection con1; +--error ER_QUERY_INTERRUPTED +reap; +eval $innodb_metrics_select; + +connection default; +CHECK TABLE t1; +INSERT INTO t1 SELECT 5 + c1, c2, c3 FROM t1; +INSERT INTO t1 SELECT 10 + c1, c2, c3 FROM t1; +INSERT INTO t1 SELECT 20 + c1, c2, c3 FROM t1; +INSERT INTO t1 SELECT 40 + c1, c2, c3 FROM t1; +# Purge may or may not have cleaned up the DELETE FROM t1 WHERE c1 = 7; +--replace_result 81 80 +EXPLAIN SELECT COUNT(*) FROM t1 WHERE c2 > 3; +ANALYZE TABLE t1; + +connection con1; +# Forge some statistics for c2d, and see that they will be used +UPDATE t1_c2_stats SET index_name = 'c2d'; +# Fake the statistics. The cardinality should be 5,80. +UPDATE t1_c2_stats SET stat_value = 2 WHERE stat_name = 'n_diff_pfx01'; +INSERT INTO t1_c2_stats +SELECT database_name, table_name, index_name, last_update, 'n_diff_pfx02', 80, +sample_size, 'c2,c1' FROM t1_c2_stats +WHERE stat_name = 'n_diff_pfx01' AND stat_description = 'c2'; +INSERT INTO mysql.innodb_index_stats SELECT * FROM t1_c2_stats; +DROP TABLE t1_c2_stats; + +CREATE INDEX c2d ON t1(c2); +# This should show the newly calculated stats by CREATE INDEX above, +# not the faked cardinality=4 for c2d(c2). +# Purge may or may not have cleaned up the DELETE FROM t1 WHERE c1 = 7; +--replace_result 81 80 +SHOW INDEX FROM t1; +EXPLAIN SELECT COUNT(*) FROM t1 WHERE c2 > 3; + +SHOW CREATE TABLE t1; + +connection default; +SET @merge_encrypt_0= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_merge_blocks_encrypted'); +SET @merge_decrypt_0= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_merge_blocks_decrypted'); +SET @rowlog_encrypt_0= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_rowlog_blocks_encrypted'); +connection con1; + +# Exceed the configured innodb_online_alter_log_max_size. +# The actual limit is a multiple of innodb_sort_buf_size, +# because that is the size of the in-memory log buffers. +SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2e_created WAIT_FOR dml2_done'; +# Ensure that the ALTER TABLE will be executed even with some concurrent DML. +SET lock_wait_timeout = 10; +--send +ALTER TABLE t1 CHANGE c2 c22 INT, DROP INDEX c2d, ADD INDEX c2e(c22, c3(10)), ALGORITHM = NOCOPY; + +# Generate some log (delete-mark, delete-unmark, insert etc.) +# while the index creation is blocked. Some of this may run +# in parallel with the clustered index scan. +connection default; +INSERT INTO t1 SELECT 80 + c1, c2, c3 FROM t1; +INSERT INTO t1 SELECT 160 + c1, c2, c3 FROM t1; +#UPDATE t1 SET c2 = c2 + 1; +SET DEBUG_SYNC = 'now WAIT_FOR c2e_created'; +# At this point, the clustered index scan must have completed, +# but the modification log keeps accumulating due to the DEBUG_SYNC. +eval $innodb_metrics_select; +let $c= 8; +while ($c) +{ + UPDATE t1 SET c2 = c2 + 1; + UPDATE t1 SET c2 = c2 + 2; + dec $c; +} +# Incomplete index c2e should exist until the DDL thread notices the overflow. +# (The output below strips TEMP_INDEX_PREFIX from the name.) +eval $innodb_metrics_select; +SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si +INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf +ON si.index_id = sf.index_id WHERE si.name = '?c2e'; + +SET @merge_encrypt_1= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_merge_blocks_encrypted'); +SET @merge_decrypt_1= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_merge_blocks_decrypted'); +SET @rowlog_encrypt_1= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_rowlog_blocks_encrypted'); + +SELECT +(@merge_encrypt_1-@merge_encrypt_0)- +(@merge_decrypt_1-@merge_decrypt_0) as sort_balance, +@merge_encrypt_1>@merge_encrypt_0, @merge_decrypt_1>@merge_decrypt_0, +@rowlog_encrypt_1>@rowlog_encrypt_0; + +# Release con1. +SET DEBUG_SYNC = 'now SIGNAL dml2_done'; + +connection con1; +# If the following fails with the wrong error, it probably means that +# you should rerun with a larger mtr --debug-sync-timeout. +--error ER_INNODB_ONLINE_LOG_TOO_BIG +reap; +# The index c2e should have been dropped from the data dictionary +# when the above error was noticed. It should still exist in the +# cache with index->online_status = ONLINE_INDEX_ABORTED_DROPPED. +eval $innodb_metrics_select; +SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si +INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf +ON si.index_id = sf.index_id WHERE si.name = 'c2e'; + +# ddl_background_drop_indexes = 1 here, because the incomplete index c2e still +# exists in the InnoDB data dictionary cache. +eval $innodb_metrics_select; + +connection default; + +ALTER TABLE t1 COMMENT 'testing if c2e will be dropped'; + +# Check that the 'zombie' index c2e was dropped. +eval $innodb_metrics_select; + +SET @merge_encrypt_1= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_merge_blocks_encrypted'); +SET @merge_decrypt_1= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_merge_blocks_decrypted'); +SET @rowlog_encrypt_1= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_rowlog_blocks_encrypted'); +SET @rowlog_decrypt_1= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_rowlog_blocks_decrypted'); + +connection con1; +# Accumulate and apply some modification log. +SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2f_created WAIT_FOR dml3_done'; +--send +ALTER TABLE t1 ADD INDEX c2f(c22f), CHANGE c2 c22f INT; + +connection default; +SET DEBUG_SYNC = 'now WAIT_FOR c2f_created'; +# Generate some log (delete-mark, delete-unmark, insert etc.) +eval $innodb_metrics_select; +let $c= 6; +while ($c) +{ +BEGIN; +INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 160; +DELETE FROM t1 WHERE c1 > 320; +COMMIT; +BEGIN; +UPDATE t1 SET c2 = c2 + 1; +COMMIT; +dec $c; +} +eval $innodb_metrics_select; +# Release con1. +SET DEBUG_SYNC = 'now SIGNAL dml3_done'; + +connection con1; +reap; + +eval $innodb_metrics_select; + +connection default; + +SET @merge_encrypt_2= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_merge_blocks_encrypted'); +SET @merge_decrypt_2= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_merge_blocks_decrypted'); +SET @rowlog_encrypt_2= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_rowlog_blocks_encrypted'); +SET @rowlog_decrypt_2= +(SELECT variable_value FROM information_schema.global_status +WHERE variable_name = 'innodb_encryption_n_rowlog_blocks_decrypted'); + +SELECT +(@merge_encrypt_2-@merge_encrypt_1)- +(@merge_decrypt_2-@merge_decrypt_1) as sort_balance, +(@rowlog_encrypt_2-@rowlog_encrypt_1)- +(@rowlog_decrypt_2-@rowlog_decrypt_1) as log_balance; +SELECT +@merge_encrypt_2-@merge_encrypt_1>0 as sort_encrypted, +@merge_decrypt_2-@merge_decrypt_1>0 as sort_decrypted, +@rowlog_encrypt_2-@rowlog_encrypt_1>0 as log_encrypted, +@rowlog_decrypt_2-@rowlog_decrypt_1>0 as log_decrypted; + +connection con1; +SELECT COUNT(c22f) FROM t1; +CHECK TABLE t1; +# Avoid a strange DEBUG_SYNC timeout on c3p5_created. +SET GLOBAL innodb_max_purge_lag_wait=0; + +# Create a column prefix index. +--error ER_DUP_ENTRY +ALTER TABLE t1 ADD UNIQUE INDEX c3p5(c3(5)); +UPDATE t1 SET c3 = NULL WHERE c3 = ''; +SET lock_wait_timeout = 1; +SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c3p5_created WAIT_FOR ins_done'; +--send +ALTER TABLE t1 ADD UNIQUE INDEX c3p5(c3(5)); + +connection default; +SET DEBUG_SYNC = 'now WAIT_FOR c3p5_created'; + +# Check that the index was created. +SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si +INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf +ON si.index_id = sf.index_id WHERE si.name = '?c3p5'; + +SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL ins_done WAIT_FOR ddl_timed_out'; +--send +INSERT INTO t1 VALUES(347,33101,NULL); + +connection con1; +--error ER_LOCK_WAIT_TIMEOUT +reap; +SET DEBUG_SYNC = 'now SIGNAL ddl_timed_out'; + +# InnoDB should have cleaned up the index c3p5 from the data dictionary, +# but not yet from the dictionary cache. +SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si +INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf +ON si.index_id = sf.index_id WHERE si.name = 'c3p5'; +eval $innodb_metrics_select; + +connection default; +reap; +# Index c3p5 should still exist in the data dictionary cache. +eval $innodb_metrics_select; + +--disable_parsing +# Temporarily disabled by fix for bug#14213236. Should be either +# removed or updated to take into account that locking for IMPORT/ +# DISCARD TABLESPACE happens on MDL layer. New test case is added +# to validate this at MDL layer(i_main.alter_table.test) + +SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2g_created WAIT_FOR dml4_done'; +# The lock upgrade at the end of the ALTER will conflict with the DISCARD. +SET lock_wait_timeout = 1; +--send +ALTER TABLE t1 DROP INDEX c2f, ADD INDEX c2g(c22f); + +connection con1; +SET DEBUG_SYNC = 'now WAIT_FOR c2g_created'; + +connect (con2,localhost,root,,); + +# This will conflict with the ALTER in connection default, above. +SET lock_wait_timeout = 10; +--send +ALTER TABLE t1 DISCARD TABLESPACE; + +connection con1; +let $wait_condition= + SELECT COUNT(*) = 1 FROM information_schema.processlist + WHERE state = 'Waiting for table level lock' and + info = 'ALTER TABLE t1 DISCARD TABLESPACE'; +--source include/wait_condition.inc + +SET DEBUG_SYNC = 'now SIGNAL dml4_done'; +disconnect con1; +connection con2; +reap; +disconnect con2; +connection default; +--error ER_LOCK_WAIT_TIMEOUT +reap; +--enable_parsing +#remove below con1 disconnect if above test case is enabled +connection default; + +SHOW CREATE TABLE t1; +ALTER TABLE t1 DROP INDEX c2d, DROP INDEX c2f; +# The ALTER TABLE should have cleaned up c3p5 from the cache. +eval $innodb_metrics_select; +ALTER TABLE t1 ADD INDEX c2h(c22f), ALGORITHM = INPLACE; +--error ER_DUP_KEYNAME +ALTER TABLE t1 ADD INDEX c2h(c22f), ALGORITHM = COPY; + +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL innodb_monitor_disable = module_ddl; + +DROP TABLE t1; + +--echo # +--echo # MDEV-13205 assertion !dict_index_is_online_ddl(index) upon ALTER TABLE +--echo # +CREATE TABLE t1 (c VARCHAR(64)) ENGINE=InnoDB; +INSERT INTO t1 VALUES('foo'); +SET DEBUG_SYNC = 'row_log_apply_before SIGNAL t1u_created WAIT_FOR dup_done'; +send ALTER TABLE t1 ADD UNIQUE(c); + +connection con1; +SET DEBUG_SYNC = 'now WAIT_FOR t1u_created'; +INSERT INTO t1 VALUES('bar'),('bar'); +SET DEBUG_SYNC = 'now SIGNAL dup_done'; + +connection default; +--error ER_DUP_ENTRY +reap; + +SET DEBUG_SYNC = 'RESET'; +disconnect con1; + +CREATE TABLE t2 (c VARCHAR(64)) ENGINE=InnoDB; +--error ER_CANT_CREATE_TABLE +ALTER TABLE t2 ADD FOREIGN KEY (c) REFERENCES t1 (c); +DROP TABLE t2,t1; + +CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB; +INSERT INTO t1 VALUES(0,0); + +connect (con1,localhost,root,,); +SET DEBUG_SYNC = 'row_log_apply_before SIGNAL created WAIT_FOR inserted'; +send ALTER TABLE t1 ADD INDEX(b); + +connection default; +SET DEBUG_SYNC = 'now WAIT_FOR created'; +BEGIN; +INSERT INTO t1 VALUES(1,1); +ROLLBACK; +SET DEBUG_SYNC = 'now SIGNAL inserted'; + +connection con1; +reap; +disconnect con1; + +connection default; +SELECT * FROM t1; +CHECK TABLE t1; +DROP TABLE t1; +SET DEBUG_SYNC = 'RESET'; + +# Check that all connections opened by test cases in this file are really +# gone so execution of other tests won't be affected by their presence. +--source include/wait_until_count_sessions.inc + +SET GLOBAL innodb_file_per_table = @global_innodb_file_per_table_orig; +--disable_warnings +SET GLOBAL innodb_monitor_enable = default; +SET GLOBAL innodb_monitor_disable = default; +--enable_warnings |