MDEV-27962 Instant DDL downgrades the MDL when table is empty

 - Server incorrectly downgrading the MDL after prepare phase when
table is empty. mdl_exclusive_after_prepare is being set in
prepare phase only. But mdl_exclusive_after_prepare condition was
misplaced and checked before prepare phase by
commit d270525d and it is now
changed to check after prepare phase.

 - main.innodb_mysql_sync test case was changed to avoid locking
optimization when table is empty.
parent 3c58cdd9
...@@ -131,6 +131,7 @@ connection default; ...@@ -131,6 +131,7 @@ connection default;
DROP DATABASE db1; DROP DATABASE db1;
# Test 2: Primary index (implicit), should block writes. # Test 2: Primary index (implicit), should block writes.
CREATE TABLE t1(a INT NOT NULL, b INT NOT NULL) engine=innodb; CREATE TABLE t1(a INT NOT NULL, b INT NOT NULL) engine=innodb;
INSERT INTO t1 VALUES(1, 2);
SET DEBUG_SYNC= "alter_table_inplace_after_lock_downgrade SIGNAL manage WAIT_FOR query"; SET DEBUG_SYNC= "alter_table_inplace_after_lock_downgrade SIGNAL manage WAIT_FOR query";
# Sending: # Sending:
ALTER TABLE t1 ADD UNIQUE INDEX(a), LOCK=SHARED; ALTER TABLE t1 ADD UNIQUE INDEX(a), LOCK=SHARED;
...@@ -139,15 +140,16 @@ SET DEBUG_SYNC= "now WAIT_FOR manage"; ...@@ -139,15 +140,16 @@ SET DEBUG_SYNC= "now WAIT_FOR manage";
USE test; USE test;
SELECT * FROM t1; SELECT * FROM t1;
a b a b
1 2
# Sending: # Sending:
UPDATE t1 SET a=NULL; UPDATE t1 SET a=3;
connection con2; connection con2;
# Waiting for SELECT to be blocked by the metadata lock on t1 # Waiting for SELECT to be blocked by the metadata lock on t1
SET DEBUG_SYNC= "now SIGNAL query"; SET DEBUG_SYNC= "now SIGNAL query";
connection default; connection default;
# Reaping: ALTER TABLE t1 ADD UNIQUE INDEX(a) # Reaping: ALTER TABLE t1 ADD UNIQUE INDEX(a)
connection con1; connection con1;
# Reaping: UPDATE t1 SET a=NULL # Reaping: UPDATE t1 SET a=3
# Test 3: Primary index (explicit), should block writes. # Test 3: Primary index (explicit), should block writes.
connection default; connection default;
ALTER TABLE t1 DROP INDEX a; ALTER TABLE t1 DROP INDEX a;
...@@ -158,15 +160,16 @@ connection con1; ...@@ -158,15 +160,16 @@ connection con1;
SET DEBUG_SYNC= "now WAIT_FOR manage"; SET DEBUG_SYNC= "now WAIT_FOR manage";
SELECT * FROM t1; SELECT * FROM t1;
a b a b
3 2
# Sending: # Sending:
UPDATE t1 SET a=NULL; UPDATE t1 SET a=4;
connection con2; connection con2;
# Waiting for SELECT to be blocked by the metadata lock on t1 # Waiting for SELECT to be blocked by the metadata lock on t1
SET DEBUG_SYNC= "now SIGNAL query"; SET DEBUG_SYNC= "now SIGNAL query";
connection default; connection default;
# Reaping: ALTER TABLE t1 ADD PRIMARY KEY (a) # Reaping: ALTER TABLE t1 ADD PRIMARY KEY (a)
connection con1; connection con1;
# Reaping: UPDATE t1 SET a=NULL # Reaping: UPDATE t1 SET a=4
# Test 4: Secondary unique index, should not block reads. # Test 4: Secondary unique index, should not block reads.
connection default; connection default;
SET DEBUG_SYNC= "alter_table_inplace_after_lock_downgrade SIGNAL manage WAIT_FOR query"; SET DEBUG_SYNC= "alter_table_inplace_after_lock_downgrade SIGNAL manage WAIT_FOR query";
...@@ -176,6 +179,7 @@ connection con1; ...@@ -176,6 +179,7 @@ connection con1;
SET DEBUG_SYNC= "now WAIT_FOR manage"; SET DEBUG_SYNC= "now WAIT_FOR manage";
SELECT * FROM t1; SELECT * FROM t1;
a b a b
4 2
SET DEBUG_SYNC= "now SIGNAL query"; SET DEBUG_SYNC= "now SIGNAL query";
connection default; connection default;
# Reaping: ALTER TABLE t1 ADD UNIQUE (b) # Reaping: ALTER TABLE t1 ADD UNIQUE (b)
......
...@@ -176,6 +176,7 @@ DROP DATABASE db1; ...@@ -176,6 +176,7 @@ DROP DATABASE db1;
--echo # Test 2: Primary index (implicit), should block writes. --echo # Test 2: Primary index (implicit), should block writes.
CREATE TABLE t1(a INT NOT NULL, b INT NOT NULL) engine=innodb; CREATE TABLE t1(a INT NOT NULL, b INT NOT NULL) engine=innodb;
INSERT INTO t1 VALUES(1, 2);
SET DEBUG_SYNC= "alter_table_inplace_after_lock_downgrade SIGNAL manage WAIT_FOR query"; SET DEBUG_SYNC= "alter_table_inplace_after_lock_downgrade SIGNAL manage WAIT_FOR query";
--echo # Sending: --echo # Sending:
--send ALTER TABLE t1 ADD UNIQUE INDEX(a), LOCK=SHARED --send ALTER TABLE t1 ADD UNIQUE INDEX(a), LOCK=SHARED
...@@ -185,13 +186,13 @@ SET DEBUG_SYNC= "now WAIT_FOR manage"; ...@@ -185,13 +186,13 @@ SET DEBUG_SYNC= "now WAIT_FOR manage";
USE test; USE test;
SELECT * FROM t1; SELECT * FROM t1;
--echo # Sending: --echo # Sending:
--send UPDATE t1 SET a=NULL --send UPDATE t1 SET a=3
connection con2; connection con2;
--echo # Waiting for SELECT to be blocked by the metadata lock on t1 --echo # Waiting for SELECT to be blocked by the metadata lock on t1
let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist
WHERE state= 'Waiting for table metadata lock' WHERE state= 'Waiting for table metadata lock'
AND info='UPDATE t1 SET a=NULL'; AND info='UPDATE t1 SET a=3';
--source include/wait_condition.inc --source include/wait_condition.inc
SET DEBUG_SYNC= "now SIGNAL query"; SET DEBUG_SYNC= "now SIGNAL query";
...@@ -200,7 +201,7 @@ connection default; ...@@ -200,7 +201,7 @@ connection default;
--reap --reap
connection con1; connection con1;
--echo # Reaping: UPDATE t1 SET a=NULL --echo # Reaping: UPDATE t1 SET a=3
--reap --reap
--echo # Test 3: Primary index (explicit), should block writes. --echo # Test 3: Primary index (explicit), should block writes.
...@@ -215,13 +216,13 @@ connection con1; ...@@ -215,13 +216,13 @@ connection con1;
SET DEBUG_SYNC= "now WAIT_FOR manage"; SET DEBUG_SYNC= "now WAIT_FOR manage";
SELECT * FROM t1; SELECT * FROM t1;
--echo # Sending: --echo # Sending:
--send UPDATE t1 SET a=NULL --send UPDATE t1 SET a=4
connection con2; connection con2;
--echo # Waiting for SELECT to be blocked by the metadata lock on t1 --echo # Waiting for SELECT to be blocked by the metadata lock on t1
let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist
WHERE state= 'Waiting for table metadata lock' WHERE state= 'Waiting for table metadata lock'
AND info='UPDATE t1 SET a=NULL'; AND info='UPDATE t1 SET a=4';
--source include/wait_condition.inc --source include/wait_condition.inc
SET DEBUG_SYNC= "now SIGNAL query"; SET DEBUG_SYNC= "now SIGNAL query";
...@@ -230,7 +231,7 @@ connection default; ...@@ -230,7 +231,7 @@ connection default;
--reap --reap
connection con1; connection con1;
--echo # Reaping: UPDATE t1 SET a=NULL --echo # Reaping: UPDATE t1 SET a=4
--reap --reap
--echo # Test 4: Secondary unique index, should not block reads. --echo # Test 4: Secondary unique index, should not block reads.
......
...@@ -462,12 +462,27 @@ INSERT INTO t1 SET a=0, i=REPEAT('1', 10000); ...@@ -462,12 +462,27 @@ INSERT INTO t1 SET a=0, i=REPEAT('1', 10000);
ROLLBACK; ROLLBACK;
set DEBUG_SYNC='now SIGNAL go'; set DEBUG_SYNC='now SIGNAL go';
connection default; connection default;
disconnect con1;
SELECT * FROM t1; SELECT * FROM t1;
a b c d e f g h i a b c d e f g h i
1 2 3 4 5 6 7 8 test 1 2 3 4 5 6 7 8 test
DROP TABLE t1; DROP TABLE t1;
SET DEBUG_SYNC=RESET; SET DEBUG_SYNC=RESET;
#
# MDEV-27962 Instant DDL downgrades the MDL when table is empty
#
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL)ENGINE=InnoDB;
SET DEBUG_SYNC="alter_table_inplace_after_lock_downgrade SIGNAL try_insert WAIT_FOR alter_progress";
ALTER TABLE t1 ADD INDEX(f1), ADD INDEX(f2);
connection con1;
SET SESSION lock_wait_timeout=1;
SET DEBUG_SYNC="now WAIT_FOR try_insert";
INSERT INTO t1 VALUES(1, 2);
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SET DEBUG_SYNC="now SIGNAL alter_progress";
disconnect con1;
connection default;
DROP TABLE t1;
SET DEBUG_SYNC=reset;
# End of 10.4 tests # End of 10.4 tests
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency; SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;
SELECT variable_value-@old_instant instants SELECT variable_value-@old_instant instants
......
...@@ -533,11 +533,28 @@ set DEBUG_SYNC='now SIGNAL go'; ...@@ -533,11 +533,28 @@ set DEBUG_SYNC='now SIGNAL go';
connection default; connection default;
reap; reap;
disconnect con1;
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
SET DEBUG_SYNC=RESET; SET DEBUG_SYNC=RESET;
--echo #
--echo # MDEV-27962 Instant DDL downgrades the MDL when table is empty
--echo #
CREATE TABLE t1(f1 INT NOT NULL, f2 INT NOT NULL)ENGINE=InnoDB;
SET DEBUG_SYNC="alter_table_inplace_after_lock_downgrade SIGNAL try_insert WAIT_FOR alter_progress";
send ALTER TABLE t1 ADD INDEX(f1), ADD INDEX(f2);
connection con1;
SET SESSION lock_wait_timeout=1;
SET DEBUG_SYNC="now WAIT_FOR try_insert";
--error ER_LOCK_WAIT_TIMEOUT
INSERT INTO t1 VALUES(1, 2);
SET DEBUG_SYNC="now SIGNAL alter_progress";
disconnect con1;
connection default;
reap;
DROP TABLE t1;
SET DEBUG_SYNC=reset;
--echo # End of 10.4 tests --echo # End of 10.4 tests
SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency; SET GLOBAL innodb_purge_rseg_truncate_frequency = @save_frequency;
......
...@@ -7716,8 +7716,7 @@ static bool mysql_inplace_alter_table(THD *thd, ...@@ -7716,8 +7716,7 @@ static bool mysql_inplace_alter_table(THD *thd,
lock for prepare phase under LOCK TABLES in the same way as when lock for prepare phase under LOCK TABLES in the same way as when
exclusive lock is required for duration of the whole statement. exclusive lock is required for duration of the whole statement.
*/ */
if (!ha_alter_info->mdl_exclusive_after_prepare && if (inplace_supported == HA_ALTER_INPLACE_EXCLUSIVE_LOCK ||
(inplace_supported == HA_ALTER_INPLACE_EXCLUSIVE_LOCK ||
((inplace_supported == HA_ALTER_INPLACE_COPY_NO_LOCK || ((inplace_supported == HA_ALTER_INPLACE_COPY_NO_LOCK ||
inplace_supported == HA_ALTER_INPLACE_COPY_LOCK || inplace_supported == HA_ALTER_INPLACE_COPY_LOCK ||
inplace_supported == HA_ALTER_INPLACE_NOCOPY_NO_LOCK || inplace_supported == HA_ALTER_INPLACE_NOCOPY_NO_LOCK ||
...@@ -7725,7 +7724,7 @@ static bool mysql_inplace_alter_table(THD *thd, ...@@ -7725,7 +7724,7 @@ static bool mysql_inplace_alter_table(THD *thd,
inplace_supported == HA_ALTER_INPLACE_INSTANT) && inplace_supported == HA_ALTER_INPLACE_INSTANT) &&
(thd->locked_tables_mode == LTM_LOCK_TABLES || (thd->locked_tables_mode == LTM_LOCK_TABLES ||
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) || thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) ||
alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)) alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
{ {
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto cleanup; goto cleanup;
...@@ -7822,7 +7821,8 @@ static bool mysql_inplace_alter_table(THD *thd, ...@@ -7822,7 +7821,8 @@ static bool mysql_inplace_alter_table(THD *thd,
necessary only for prepare phase (unless we are not under LOCK TABLES) and necessary only for prepare phase (unless we are not under LOCK TABLES) and
user has not explicitly requested exclusive lock. user has not explicitly requested exclusive lock.
*/ */
if ((inplace_supported == HA_ALTER_INPLACE_COPY_NO_LOCK || if (!ha_alter_info->mdl_exclusive_after_prepare &&
(inplace_supported == HA_ALTER_INPLACE_COPY_NO_LOCK ||
inplace_supported == HA_ALTER_INPLACE_COPY_LOCK || inplace_supported == HA_ALTER_INPLACE_COPY_LOCK ||
inplace_supported == HA_ALTER_INPLACE_NOCOPY_LOCK || inplace_supported == HA_ALTER_INPLACE_NOCOPY_LOCK ||
inplace_supported == HA_ALTER_INPLACE_NOCOPY_NO_LOCK) && inplace_supported == HA_ALTER_INPLACE_NOCOPY_NO_LOCK) &&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment