1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

MDEV-11227 - mysqlimport -l doesn't issue UNLOCK TABLES

Implementation of MDEV-7660 introduced unwanted incompatible change:
modifications under LOCK TABLES with autocommit enabled are rolled back on
disconnect. Previously everything was committed, because LOCK TABLES didn't
adjust autocommit setting.

This patch restores original behavior by reverting some changes done in
MDEV-7660:
- sql/sql_parse.cc: do not reset autocommit on LOCK TABLES
- sql/sql_base.cc: do not set autocommit on UNLOCK TABLES
- test cases: main.lock_tables_lost_commit, main.partition_explicit_prune,
  rpl.rpl_switch_stm_row_mixed, tokudb.nested_txn_implicit_commit,
  tokudb_bugs.db806

But it makes InnoDB tables under LOCK TABLES ... READ [LOCAL] not protected
against DML. To restore protection some changes from WL#6671 were merged,
specifically MDL_SHARED_READ_ONLY and test cases.

WL#6671 merge highlights:
- Not all tests merged.
- In MySQL LOCK TABLES ... READ acquires MDL_SHARED_READ_ONLY for all engines,
  in MariaDB MDL_SHARED_READ is always acquired first and then upgraded to
  MDL_SHARED_READ_ONLY for InnoDB only.
- The above allows us to omit MDL_SHARED_WRITE_LOW_PRIO implementation in
  MariaDB, which is rather useless with InnoDB. In MySQL it is needed to
  preserve locking behavior between low priority writes and LOCK TABLES ... READ
  for non-InnoDB engines (covered by sys_vars.sql_low_priority_updates_func).
- Omitted HA_NO_READ_LOCAL_LOCK, we rely on lock_count() instead.
- Omitted "piglets": in MariaDB stream of DML against InnoDB table may lead to
  concurrent LOCK TABLES ... READ starvation.
- HANDLER ... OPEN acquires MDL_SHARED_READ instead of MDL_SHARED in MariaDB.
- Omitted SNRW->X MDL lock upgrade for IMPORT/DISCARD TABLESPAECE under LOCK
  TABLES.
- Omitted strong locks for views, triggers and SP under LOCK TABLES.
- Omitted IX schema lock for LOCK TABLES READ.
- Omitted deadlock weight juggling for LOCK TABLES.

Full WL#6671 merge status:
- innodb.innodb-lock: fully merged
- main.alter_table: not merged due to different HANDLER solution
- main.debug_sync: fully merged
- main.handler_innodb: not merged due to different HANDLER solution
- main.handler_myisam: not merged due to different HANDLER solution
- main.innodb_mysql_lock: fully merged
- main.insert_notembedded: fully merged
- main.lock: not merged (due to no strong locks for views)
- main.lock_multi: not merged
- main.lock_sync: fully merged (partially in MDEV-7660)
- main.mdl_sync: not merged
- main.partition_debug_sync: not merged due to different HANDLER solution
- main.status: fully merged
- main.view: fully merged
- perfschema.mdl_func: not merged (no such test in MariaDB)
- perfschema.table_aggregate_global_2u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_aggregate_global_2u_3t: not merged (didn't fail in MariaDB)
- perfschema.table_aggregate_global_4u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_aggregate_global_4u_3t: not merged (didn't fail in MariaDB)
- perfschema.table_aggregate_hist_2u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_aggregate_hist_2u_3t: not merged (didn't fail in MariaDB)
- perfschema.table_aggregate_hist_4u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_aggregate_hist_4u_3t: not merged (didn't fail in MariaDB)
- perfschema.table_aggregate_thread_2u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_aggregate_thread_2u_3t: not merged (didn't fail in MariaDB)
- perfschema.table_aggregate_thread_4u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_aggregate_thread_4u_3t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_global_2u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_global_2u_3t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_global_4u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_global_4u_3t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_hist_2u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_hist_2u_3t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_hist_4u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_hist_4u_3t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_thread_2u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_thread_2u_3t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_thread_4u_2t: not merged (didn't fail in MariaDB)
- perfschema.table_lock_aggregate_thread_4u_3t: not merged (didn't fail in MariaDB)
- sys_vars.sql_low_priority_updates_func: not merged
- include/thr_rwlock.h: not merged, rw_pr_lock_assert_write_owner and
  rw_pr_lock_assert_not_write_owner are macros in MariaDB
- sql/handler.h: not merged (HA_NO_READ_LOCAL_LOCK)
- sql/mdl.cc: partially merged (MDL_SHARED_READ_ONLY only)
- sql/mdl.h: partially merged (MDL_SHARED_READ_ONLY only)
- sql/lock.cc: fully merged
- sql/sp_head.cc: not merged
- sql/sp_head.h: not merged
- sql/sql_base.cc: partially merged (MDL_SHARED_READ_ONLY only)
- sql/sql_base.h: not merged
- sql/sql_class.cc: fully merged
- sql/sql_class.h: fully merged
- sql/sql_handler.cc: merged partially (different solution in MariaDB)
- sql/sql_parse.cc: partially merged, mostly omitted low priority write part
- sql/sql_reload.cc: not merged comment change
- sql/sql_table.cc: not merged SNRW->X upgrade for IMPORT/DISCARD TABLESPACE
- sql/sql_view.cc: not merged
- sql/sql_yacc.yy: not merged (MDL_SHARED_WRITE_LOW_PRIO, MDL_SHARED_READ_ONLY)
- sql/table.cc: not merged (MDL_SHARED_WRITE_LOW_PRIO)
- sql/table.h: not merged (MDL_SHARED_WRITE_LOW_PRIO)
- sql/trigger.cc: not merged
- storage/innobase/handler/ha_innodb.cc: merged store_lock()/lock_count()
  changes (in MDEV-7660), didn't merge HA_NO_READ_LOCAL_LOCK
- storage/innobase/handler/ha_innodb.h: fully merged in MDEV-7660
- storage/myisammrg/ha_myisammrg.cc: not merged comment change
- storage/perfschema/table_helper.cc: not merged (no MDL support in MariaDB PFS)
- unittest/gunit/mdl-t.cc: not merged
- unittest/gunit/mdl_sync-t.cc: not merged

MariaDB specific changes:
- handler.heap: different HANDLER solution, MDEV-7660
- handler.innodb: different HANDLER solution, MDEV-7660
- handler.interface: different HANDLER solution, MDEV-7660
- handler.myisam: different HANDLER solution, MDEV-7660
- main.mdl_sync: MDEV-7660 specific changes
- main.partition_debug_sync: removed test due to different HANDLER solution,
  MDEV-7660
- main.truncate_coverage: removed test due to different HANDLER solution,
  MDEV-7660
- mysql-test/include/mtr_warnings.sql: additional cleanup, MDEV-7660
- mysql-test/lib/v1/mtr_report.pl: additional cleanup, MDEV-7660
- plugin/metadata_lock_info/metadata_lock_info.cc: not in MySQL
- sql/sql_handler.cc: MariaDB specific fix for mysql_ha_read(), MDEV-7660
This commit is contained in:
Sergey Vojtovich
2016-12-08 14:20:46 +04:00
parent 561b6d213c
commit 8774a02364
30 changed files with 538 additions and 353 deletions

View File

@ -265,17 +265,31 @@ connection default;
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (c1 INT);
LOCK TABLE t1 READ;
CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
INSERT INTO t1 VALUES (1);
SELECT GET_LOCK('mysqltest_lock', 100);
GET_LOCK('mysqltest_lock', 100)
1
connect con1,localhost,root,,;
# Sending:
UPDATE t1 SET c1=GET_LOCK('mysqltest_lock', 100);;
connect con2,localhost,root,,;
SET DEBUG_SYNC= 'wait_for_lock SIGNAL locked EXECUTE 2';
INSERT INTO t1 VALUES (1);
connection default;
SET DEBUG_SYNC= 'now WAIT_FOR locked';
UNLOCK TABLES;
SELECT RELEASE_LOCK('mysqltest_lock');
RELEASE_LOCK('mysqltest_lock')
1
connection con1;
# Reaping UPDATE
SELECT RELEASE_LOCK('mysqltest_lock');
RELEASE_LOCK('mysqltest_lock')
1
connection con2;
retrieve INSERT result.
disconnect con1;
disconnect con2;
connection default;
DROP TABLE t1;
SET DEBUG_SYNC= 'RESET';

View File

@ -36,6 +36,7 @@ connection con3;
set @@autocommit=1;
connection default;
disconnect con1;
disconnect con2;
disconnect con3;
#
# Test for bug #37346 "innodb does not detect deadlock between update
@ -72,34 +73,6 @@ connection default;
disconnect con37346;
drop table t1;
#
# Bug #42147 Concurrent DML and LOCK TABLE ... READ for InnoDB
# table cause warnings in errlog
#
#
# Note that this test for now relies on a global suppression of
# the warning "Found lock of type 6 that is write and read locked"
# This suppression rule can be removed once Bug#42147 is properly
# fixed. See bug page for more info.
#
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (i INT) engine= innodb;
# Get user-level lock
connection con2;
SELECT get_lock('bug42147_lock', 60);
get_lock('bug42147_lock', 60)
1
connection default;
INSERT INTO t1 SELECT get_lock('bug42147_lock', 60);
connection con2;
LOCK TABLES t1 READ;
SELECT release_lock('bug42147_lock');
release_lock('bug42147_lock')
1
UNLOCK TABLES;
connection default;
disconnect con2;
DROP TABLE t1;
#
# Bug#53798 OPTIMIZE TABLE breaks repeatable read
#
DROP TABLE IF EXISTS t1;

View File

@ -119,33 +119,5 @@ DROP USER user20989@localhost;
disconnect root;
connection default;
DROP DATABASE meow;
connection: default
set low_priority_updates=1;
drop table if exists t1;
create table t1 (a int, b int, unique key t1$a (a));
lock table t1 read;
connect update,localhost,root,,;
connection update;
connection: update
set low_priority_updates=1;
show variables like 'low_priority_updates';
Variable_name Value
low_priority_updates ON
insert into t1 values (1, 2) ON DUPLICATE KEY UPDATE b = 2;;
connection default;
connect select,localhost,root,,;
connection: select
select * from t1;
a b
connection default;
connection: default
select * from t1;
a b
connection default;
disconnect update;
disconnect select;
unlock tables;
drop table t1;
set low_priority_updates=default;
set local sql_mode=default;
set global sql_mode=default;

View File

@ -784,3 +784,74 @@ DROP VIEW v1;
DROP TABLE t1;
disconnect con1;
disconnect con2;
#
# Bug#28587 SELECT is blocked by INSERT waiting on read lock, even with low_priority_updates
#
set low_priority_updates=1;
drop table if exists t1;
drop table if exists t2;
set debug_sync='RESET';
create table t1 (a int, b int, unique key t1$a (a));
create table t2 (j int, k int);
set debug_sync='after_lock_tables_takes_lock SIGNAL parked WAIT_FOR go';
# Sending:
insert into t2 select * from t1;;
connect update,localhost,root,,;
connection update;
set debug_sync='now WAIT_FOR parked';
set low_priority_updates=1;
show variables like 'low_priority_updates';
Variable_name Value
low_priority_updates ON
insert into t1 values (1, 2) ON DUPLICATE KEY UPDATE b = 2;;
connect select,localhost,root,,;
select * from t1;
a b
set debug_sync='now SIGNAL go';
connection default;
disconnect update;
disconnect select;
# Reaping INSERT SELECT
drop tables t1, t2;
set low_priority_updates=default;
set debug_sync='RESET';
#
# Additional test coverage for LOCK TABLES ... READ LOCAL
# for InnoDB tables.
#
# Check that we correctly handle deadlocks which can occur
# during metadata lock upgrade which happens when one tries
# to use LOCK TABLES ... READ LOCAL for InnoDB tables.
CREATE TABLE t1 (i INT) ENGINE=InnoDB;
CREATE TABLE t2 (j INT) ENGINE=InnoDB;
# Execute LOCK TABLE READ LOCK which will pause after acquiring
# SR metadata lock and before upgrading it to SRO lock.
SET DEBUG_SYNC="after_open_table_mdl_shared SIGNAL locked WAIT_FOR go";
# Sending:
LOCK TABLE t1 READ LOCAL;
connect con1, localhost, root;
SET DEBUG_SYNC="now WAIT_FOR locked";
# Execute RENAME TABLE which will try to acquire X lock.
# Sending:
RENAME TABLE t1 TO t3, t2 TO t1, t3 TO t2;
connect con2, localhost, root;
# Wait until RENAME TABLE is blocked.
# Resume LOCK TABLE statement. It should try to
# upgrade SR lock to SRO lock which will create
# deadlock due to presence of pending X lock.
# Deadlock should be detected and LOCK TABLES should
# release its MDL and retry opening of tables.
SET DEBUG_SYNC="now SIGNAL go";
connection con1;
# RENAME TABLE should be able to complete. Reap it.
connection default;
# Reap LOCK TABLES.
# Check that we see new version of table.
SELECT * FROM t1;
j
UNLOCK TABLES;
# Clean-up.
SET DEBUG_SYNC="RESET";
disconnect con1;
disconnect con2;
DROP TABLES t1, t2;

View File

@ -9,6 +9,7 @@ disconnect con1;
connection con2;
SELECT * FROM t1;
a
10
DROP TABLE t1;
connection default;
disconnect con2;

View File

@ -281,7 +281,7 @@ UNLOCK TABLES;
SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0;
VARIABLE_NAME VARIABLE_VALUE
HANDLER_COMMIT 3
HANDLER_COMMIT 2
HANDLER_READ_RND_NEXT 54
HANDLER_TMP_WRITE 75
HANDLER_WRITE 2
@ -440,7 +440,7 @@ UNLOCK TABLES;
SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0;
VARIABLE_NAME VARIABLE_VALUE
HANDLER_COMMIT 6
HANDLER_COMMIT 5
HANDLER_READ_FIRST 3
HANDLER_READ_NEXT 4
HANDLER_READ_RND_NEXT 108
@ -665,7 +665,7 @@ UNLOCK TABLES;
SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0;
VARIABLE_NAME VARIABLE_VALUE
HANDLER_COMMIT 6
HANDLER_COMMIT 5
HANDLER_DELETE 2
HANDLER_READ_FIRST 1
HANDLER_READ_KEY 3
@ -758,7 +758,7 @@ UNLOCK TABLES;
SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0;
VARIABLE_NAME VARIABLE_VALUE
HANDLER_COMMIT 3
HANDLER_COMMIT 2
HANDLER_READ_RND_NEXT 54
HANDLER_TMP_WRITE 75
HANDLER_WRITE 10
@ -953,7 +953,7 @@ UNLOCK TABLES;
SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0;
VARIABLE_NAME VARIABLE_VALUE
HANDLER_COMMIT 4
HANDLER_COMMIT 3
HANDLER_DELETE 1
HANDLER_READ_KEY 2
HANDLER_READ_RND 2
@ -1039,7 +1039,7 @@ UNLOCK TABLES;
SELECT * FROM INFORMATION_SCHEMA.SESSION_STATUS
WHERE VARIABLE_NAME LIKE 'HANDLER_%' AND VARIABLE_VALUE > 0;
VARIABLE_NAME VARIABLE_VALUE
HANDLER_COMMIT 4
HANDLER_COMMIT 3
HANDLER_DELETE 2
HANDLER_READ_KEY 3
HANDLER_READ_NEXT 1

View File

@ -4,6 +4,7 @@ SET @old_log_output = @@global.log_output;
SET GLOBAL LOG_OUTPUT = 'FILE';
connect con1,localhost,root,,;
connect con2,localhost,root,,;
connection default;
flush status;
show status like 'Table_lock%';
Variable_name Value
@ -13,33 +14,42 @@ select * from information_schema.session_status where variable_name like 'Table_
VARIABLE_NAME VARIABLE_VALUE
TABLE_LOCKS_IMMEDIATE 0
TABLE_LOCKS_WAITED 0
connection con1;
set sql_log_bin=0;
set @old_general_log = @@global.general_log;
set global general_log = 'OFF';
drop table if exists t1;
create table t1(n int) engine=myisam;
insert into t1 values(1);
select 1;
1
select get_lock('mysqltest_lock', 100);
get_lock('mysqltest_lock', 100)
1
connection con2;
lock tables t1 read;
unlock tables;
lock tables t1 read;
# Sending:
update t1 set n = get_lock('mysqltest_lock', 100);
connection con1;
# Wait for the first UPDATE to get blocked.
# Sending:
update t1 set n = 3;
connection default;
# wait for the second UPDATE to get blocked
select release_lock('mysqltest_lock');
release_lock('mysqltest_lock')
1
connection con2;
unlock tables;
# Reaping first UPDATE
select release_lock('mysqltest_lock');
release_lock('mysqltest_lock')
1
connection con1;
# Reaping second UPDATE
show status like 'Table_locks_waited';
Variable_name Value
Table_locks_waited 1
connection default;
drop table t1;
set global general_log = @old_general_log;
disconnect con2;
disconnect con1;
connection default;
select 1;
1
1

View File

@ -4792,21 +4792,24 @@ DROP TABLE t1, t2;
# Bug#48315 Metadata lock is not taken for merged views that
# use an INFORMATION_SCHEMA table
#
DROP TABLE IF EXISTS t1;
DROP VIEW IF EXISTS v1;
DROP PROCEDURE IF EXISTS p1;
connect con2, localhost, root;
connect con3, localhost, root;
connection default;
CREATE VIEW v1 AS SELECT schema_name FROM information_schema.schemata;
CREATE TABLE t1 (str VARCHAR(50));
CREATE PROCEDURE p1() INSERT INTO t1 SELECT * FROM v1;
CREATE PROCEDURE p1() SELECT COUNT(*), GET_LOCK('blocker', 100) FROM v1;
# CALL p1() so the view is merged.
CALL p1();
SELECT RELEASE_LOCK('blocker');
RELEASE_LOCK('blocker')
1
connection con3;
LOCK TABLE t1 READ;
SELECT GET_LOCK('blocker', 100);
GET_LOCK('blocker', 100)
1
connection default;
# Try to CALL p1() again, this time it should block for t1.
# Try to CALL p1() again, this time it should block on "blocker".
# Sending:
CALL p1();
connection con2;
@ -4815,14 +4818,18 @@ connection con2;
DROP VIEW v1;
connection con3;
# Now allow CALL p1() to complete
UNLOCK TABLES;
SELECT RELEASE_LOCK('blocker');
RELEASE_LOCK('blocker')
1
connection default;
# Reaping: CALL p1()
SELECT RELEASE_LOCK('blocker');
RELEASE_LOCK('blocker')
1
connection con2;
# Reaping: DROP VIEW v1
connection default;
DROP PROCEDURE p1;
DROP TABLE t1;
disconnect con2;
disconnect con3;
#

View File

@ -36,8 +36,11 @@ drop table t1;
# Old lock method (where LOCK TABLE was ignored by InnoDB) no longer
# works when LOCK TABLE ... WRITE is used due to fix for bugs #46272
# "MySQL 5.4.4, new MDL: unnecessary and bug #37346 "innodb does not
# detect deadlock between update and alter table". But it still works
# for LOCK TABLE ... READ.
# detect deadlock between update and alter table".
# After WL#6671 "Improve scalability by not using thr_lock.c locks
# for InnoDB tables" was implemented it no longer works for LOCK TABLES
# ,,, READ as well.
# LOCK TABLES locks are now completely handled by MDL subsystem.
#
set @@innodb_table_locks=0;
create table t1 (id integer primary key, x integer) engine=INNODB;
@ -73,28 +76,26 @@ select * from t1 where id = 0 for update;
id x
0 1
connection con2;
# The below statement should not be blocked as LOCK TABLES ... READ
# does not take strong SQL-level lock on t1. SELECTs which do not
# conflict with transaction in the first connections should not be
# blocked.
lock table t1 read;
select * from t1;
id x
0 1
1 1
2 2
select * from t1 where id = 1 lock in share mode;
id x
1 1
unlock tables;
select * from t1;
id x
0 1
1 1
2 2
commit;
# The following statement should block because SQL-level lock
# is taken on t1 which will wait until concurrent transaction
# is commited.
# Sending:
lock table t1 read;;
connection con1;
# Wait until LOCK TABLE is blocked on SQL-level lock.
# We should be able to do UPDATEs and SELECTs within transaction.
update t1 set x=2 where id = 0;
select * from t1;
id x
0 2
1 1
2 2
# Unblock LOCK TABLE.
commit;
connection con2;
# Reap LOCK TABLE.
unlock tables;
connection default;
drop table t1;
#
#Bug#12842206 INNODB LOCKING REGRESSION FOR INSERT IGNORE

View File

@ -60,8 +60,11 @@ drop table t1;
--echo # Old lock method (where LOCK TABLE was ignored by InnoDB) no longer
--echo # works when LOCK TABLE ... WRITE is used due to fix for bugs #46272
--echo # "MySQL 5.4.4, new MDL: unnecessary and bug #37346 "innodb does not
--echo # detect deadlock between update and alter table". But it still works
--echo # for LOCK TABLE ... READ.
--echo # detect deadlock between update and alter table".
--echo # After WL#6671 "Improve scalability by not using thr_lock.c locks
--echo # for InnoDB tables" was implemented it no longer works for LOCK TABLES
--echo # ,,, READ as well.
--echo # LOCK TABLES locks are now completely handled by MDL subsystem.
--echo #
set @@innodb_table_locks=0;
@ -104,20 +107,32 @@ connection con1;
select * from t1 where id = 0 for update;
connection con2;
--echo # The below statement should not be blocked as LOCK TABLES ... READ
--echo # does not take strong SQL-level lock on t1. SELECTs which do not
--echo # conflict with transaction in the first connections should not be
--echo # blocked.
lock table t1 read;
select * from t1;
select * from t1 where id = 1 lock in share mode;
unlock tables;
select * from t1;
commit;
--echo # The following statement should block because SQL-level lock
--echo # is taken on t1 which will wait until concurrent transaction
--echo # is commited.
--echo # Sending:
--send lock table t1 read;
connection con1;
--echo # Wait until LOCK TABLE is blocked on SQL-level lock.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table metadata lock" and
info = "lock table t1 read";
--source include/wait_condition.inc
--echo # We should be able to do UPDATEs and SELECTs within transaction.
update t1 set x=2 where id = 0;
select * from t1;
--echo # Unblock LOCK TABLE.
commit;
connection con2;
--echo # Reap LOCK TABLE.
--reap
unlock tables;
connection default;
drop table t1;
# End of 4.1 tests

View File

@ -410,7 +410,6 @@ LOCK TABLES t11 WRITE;
SET SESSION BINLOG_FORMAT=ROW;
INSERT INTO t11 VALUES('Several Species of Small Furry Animals Gathered Together in a Cave and Grooving With a Pict');
SET SESSION BINLOG_FORMAT=STATEMENT;
ERROR HY000: Cannot modify @@session.binlog_format inside a transaction
INSERT INTO t11 VALUES('Careful With That Axe, Eugene');
UNLOCK TABLES;
SELECT * FROM t11;

View File

@ -524,7 +524,6 @@ CREATE TABLE t11 (song VARCHAR(255));
LOCK TABLES t11 WRITE;
SET SESSION BINLOG_FORMAT=ROW;
INSERT INTO t11 VALUES('Several Species of Small Furry Animals Gathered Together in a Cave and Grooving With a Pict');
--error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT
SET SESSION BINLOG_FORMAT=STATEMENT;
INSERT INTO t11 VALUES('Careful With That Axe, Eugene');
UNLOCK TABLES;

View File

@ -381,21 +381,40 @@ DROP TABLE IF EXISTS t1;
--enable_warnings
#
# Test.
CREATE TABLE t1 (c1 INT);
LOCK TABLE t1 READ;
CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
INSERT INTO t1 VALUES (1);
SELECT GET_LOCK('mysqltest_lock', 100);
connect (con1,localhost,root,,);
--echo # Sending:
--send UPDATE t1 SET c1=GET_LOCK('mysqltest_lock', 100);
connect (con2,localhost,root,,);
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "User lock" and
info = "UPDATE t1 SET c1=GET_LOCK('mysqltest_lock', 100)";
--source include/wait_condition.inc
# Retain action after use. First used by general_log.
SET DEBUG_SYNC= 'wait_for_lock SIGNAL locked EXECUTE 2';
send INSERT INTO t1 VALUES (1);
connection default;
# Wait until INSERT waits for lock.
SET DEBUG_SYNC= 'now WAIT_FOR locked';
# let INSERT continue.
UNLOCK TABLES;
# let UPDATE continue.
SELECT RELEASE_LOCK('mysqltest_lock');
connection con1;
--echo # Reaping UPDATE
reap;
SELECT RELEASE_LOCK('mysqltest_lock');
connection con2;
--echo retrieve INSERT result.
reap;
disconnect con1;
disconnect con2;
connection default;
DROP TABLE t1;

View File

@ -64,6 +64,7 @@ set @@autocommit=1;
connection default;
disconnect con1;
disconnect con2;
disconnect con3;
@ -116,54 +117,6 @@ connection default;
disconnect con37346;
drop table t1;
--echo #
--echo # Bug #42147 Concurrent DML and LOCK TABLE ... READ for InnoDB
--echo # table cause warnings in errlog
--echo #
--echo #
--echo # Note that this test for now relies on a global suppression of
--echo # the warning "Found lock of type 6 that is write and read locked"
--echo # This suppression rule can be removed once Bug#42147 is properly
--echo # fixed. See bug page for more info.
--echo #
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
CREATE TABLE t1 (i INT) engine= innodb;
--echo # Get user-level lock
connection con2;
SELECT get_lock('bug42147_lock', 60);
connection default;
--send INSERT INTO t1 SELECT get_lock('bug42147_lock', 60)
connection con2;
let $wait_condition=
SELECT COUNT(*) > 0 FROM information_schema.processlist
WHERE state = 'User lock'
AND info = 'INSERT INTO t1 SELECT get_lock(\'bug42147_lock\', 60)';
--source include/wait_condition.inc
LOCK TABLES t1 READ;
SELECT release_lock('bug42147_lock');
let $wait_condition=
SELECT COUNT(*) > 0 FROM information_schema.processlist
WHERE state = 'executing'
AND info = 'INSERT INTO t1 SELECT get_lock(\'bug42147_lock\', 60)';
--source include/wait_condition.inc
UNLOCK TABLES;
connection default;
--reap
disconnect con2;
DROP TABLE t1;
--echo #
--echo # Bug#53798 OPTIMIZE TABLE breaks repeatable read
--echo #

View File

@ -156,41 +156,5 @@ connection default;
DROP DATABASE meow;
#
# Bug#28587 SELECT is blocked by INSERT waiting on read lock, even with low_priority_updates
#
--echo connection: default
set low_priority_updates=1;
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1 (a int, b int, unique key t1$a (a));
lock table t1 read;
connect (update,localhost,root,,);
connection update;
--echo connection: update
set low_priority_updates=1;
show variables like 'low_priority_updates';
let $ID= `select connection_id()`;
--send insert into t1 values (1, 2) ON DUPLICATE KEY UPDATE b = 2;
connection default;
# we must wait till the insert opens and locks the table
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table level lock" and id = $ID;
--source include/wait_condition.inc
connect (select,localhost,root,,);
--echo connection: select
select * from t1;
connection default;
--echo connection: default
select * from t1;
connection default;
disconnect update;
disconnect select;
unlock tables;
drop table t1;
set low_priority_updates=default;
set local sql_mode=default;
set global sql_mode=default;

View File

@ -998,6 +998,102 @@ DROP TABLE t1;
disconnect con1;
disconnect con2;
--echo #
--echo # Bug#28587 SELECT is blocked by INSERT waiting on read lock, even with low_priority_updates
--echo #
set low_priority_updates=1;
--disable_warnings
drop table if exists t1;
drop table if exists t2;
--enable_warnings
set debug_sync='RESET';
create table t1 (a int, b int, unique key t1$a (a));
create table t2 (j int, k int);
set debug_sync='after_lock_tables_takes_lock SIGNAL parked WAIT_FOR go';
--echo # Sending:
--send insert into t2 select * from t1;
connect (update,localhost,root,,);
connection update;
set debug_sync='now WAIT_FOR parked';
set low_priority_updates=1;
show variables like 'low_priority_updates';
let $ID= `select connection_id()`;
--send insert into t1 values (1, 2) ON DUPLICATE KEY UPDATE b = 2;
connect (select,localhost,root,,);
# we must wait till the insert opens and locks the table
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table level lock" and id = $ID;
--source include/wait_condition.inc
select * from t1;
set debug_sync='now SIGNAL go';
connection default;
disconnect update;
disconnect select;
--echo # Reaping INSERT SELECT
--reap
drop tables t1, t2;
set low_priority_updates=default;
set debug_sync='RESET';
--echo #
--echo # Additional test coverage for LOCK TABLES ... READ LOCAL
--echo # for InnoDB tables.
--echo #
--echo # Check that we correctly handle deadlocks which can occur
--echo # during metadata lock upgrade which happens when one tries
--echo # to use LOCK TABLES ... READ LOCAL for InnoDB tables.
--enable_connect_log
CREATE TABLE t1 (i INT) ENGINE=InnoDB;
CREATE TABLE t2 (j INT) ENGINE=InnoDB;
--echo # Execute LOCK TABLE READ LOCK which will pause after acquiring
--echo # SR metadata lock and before upgrading it to SRO lock.
SET DEBUG_SYNC="after_open_table_mdl_shared SIGNAL locked WAIT_FOR go";
--echo # Sending:
--send LOCK TABLE t1 READ LOCAL
connect (con1, localhost, root);
SET DEBUG_SYNC="now WAIT_FOR locked";
--echo # Execute RENAME TABLE which will try to acquire X lock.
--echo # Sending:
--send RENAME TABLE t1 TO t3, t2 TO t1, t3 TO t2
connect (con2, localhost, root);
--echo # Wait until RENAME TABLE is blocked.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for table metadata lock" and
info = "RENAME TABLE t1 TO t3, t2 TO t1, t3 TO t2";
--source include/wait_condition.inc
--echo # Resume LOCK TABLE statement. It should try to
--echo # upgrade SR lock to SRO lock which will create
--echo # deadlock due to presence of pending X lock.
--echo # Deadlock should be detected and LOCK TABLES should
--echo # release its MDL and retry opening of tables.
SET DEBUG_SYNC="now SIGNAL go";
connection con1;
--echo # RENAME TABLE should be able to complete. Reap it.
--reap
connection default;
--echo # Reap LOCK TABLES.
--reap
--echo # Check that we see new version of table.
SELECT * FROM t1;
UNLOCK TABLES;
--echo # Clean-up.
SET DEBUG_SYNC="RESET";
disconnect con1;
disconnect con2;
DROP TABLES t1, t2;
--disable_connect_log
# 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

View File

@ -23,13 +23,13 @@ SET GLOBAL LOG_OUTPUT = 'FILE';
connect (con1,localhost,root,,);
connect (con2,localhost,root,,);
connection default;
flush status;
show status like 'Table_lock%';
select * from information_schema.session_status where variable_name like 'Table_lock%';
connection con1;
set sql_log_bin=0;
set @old_general_log = @@global.general_log;
set global general_log = 'OFF';
@ -39,35 +39,46 @@ drop table if exists t1;
create table t1(n int) engine=myisam;
insert into t1 values(1);
# Execute dummy select in order to ensure that tables used in the
# previous statement are unlocked and closed.
select 1;
select get_lock('mysqltest_lock', 100);
connection con2;
lock tables t1 read;
unlock tables;
lock tables t1 read;
--echo # Sending:
--send update t1 set n = get_lock('mysqltest_lock', 100)
connection con1;
--echo # Wait for the first UPDATE to get blocked.
let $wait_condition= select count(*) from INFORMATION_SCHEMA.PROCESSLIST
where STATE = "User lock" and
INFO = "update t1 set n = get_lock('mysqltest_lock', 100)";
--source include/wait_condition.inc
let $ID= `select connection_id()`;
--echo # Sending:
--send update t1 set n = 3
connection con2;
# wait for the other query to start executing
connection default;
--echo # wait for the second UPDATE to get blocked
let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST
where ID = $ID and STATE = "Waiting for table level lock";
--source include/wait_condition.inc
unlock tables;
select release_lock('mysqltest_lock');
connection con2;
--echo # Reaping first UPDATE
--reap
select release_lock('mysqltest_lock');
connection con1;
--echo # Reaping second UPDATE
reap;
show status like 'Table_locks_waited';
connection default;
drop table t1;
set global general_log = @old_general_log;
disconnect con2;
disconnect con1;
connection default;
# End of 4.1 tests

View File

@ -4636,7 +4636,6 @@ DROP TABLE t1, t2;
--echo #
--disable_warnings
DROP TABLE IF EXISTS t1;
DROP VIEW IF EXISTS v1;
DROP PROCEDURE IF EXISTS p1;
--enable_warnings
@ -4647,25 +4646,27 @@ connect (con3, localhost, root);
connection default;
CREATE VIEW v1 AS SELECT schema_name FROM information_schema.schemata;
CREATE TABLE t1 (str VARCHAR(50));
CREATE PROCEDURE p1() INSERT INTO t1 SELECT * FROM v1;
CREATE PROCEDURE p1() SELECT COUNT(*), GET_LOCK('blocker', 100) FROM v1;
--echo # CALL p1() so the view is merged.
--disable_result_log
CALL p1();
--enable_result_log
SELECT RELEASE_LOCK('blocker');
connection con3;
LOCK TABLE t1 READ;
SELECT GET_LOCK('blocker', 100);
connection default;
--echo # Try to CALL p1() again, this time it should block for t1.
--echo # Try to CALL p1() again, this time it should block on "blocker".
--echo # Sending:
--send CALL p1()
connection con2;
let $wait_condition=
SELECT COUNT(*) = 1 from information_schema.processlist
WHERE state = "Waiting for table level lock" AND
info = "INSERT INTO t1 SELECT * FROM v1";
WHERE state = "User lock" AND
info = "SELECT COUNT(*), GET_LOCK('blocker', 100) FROM v1";
--source include/wait_condition.inc
--echo # ... then try to drop the view. This should block.
--echo # Sending:
@ -4677,11 +4678,14 @@ let $wait_condition=
WHERE state = "Waiting for table metadata lock" AND info = "DROP VIEW v1";
--source include/wait_condition.inc
--echo # Now allow CALL p1() to complete
UNLOCK TABLES;
SELECT RELEASE_LOCK('blocker');
connection default;
--echo # Reaping: CALL p1()
--disable_result_log
--reap
--enable_result_log
SELECT RELEASE_LOCK('blocker');
connection con2;
--echo # Reaping: DROP VIEW v1
@ -4689,7 +4693,6 @@ connection con2;
connection default;
DROP PROCEDURE p1;
DROP TABLE t1;
disconnect con2;
disconnect con3;

View File

@ -39,6 +39,7 @@ static const LEX_STRING metadata_lock_info_lock_mode[] = {
{ C_STRING_WITH_LEN("MDL_SHARED_READ") },
{ C_STRING_WITH_LEN("MDL_SHARED_WRITE") },
{ C_STRING_WITH_LEN("MDL_SHARED_UPGRADABLE") },
{ C_STRING_WITH_LEN("MDL_SHARED_READ_ONLY") },
{ C_STRING_WITH_LEN("MDL_SHARED_NO_WRITE") },
{ C_STRING_WITH_LEN("MDL_SHARED_NO_READ_WRITE") },
{ C_STRING_WITH_LEN("MDL_EXCLUSIVE") },

View File

@ -164,18 +164,12 @@ lock_tables_check(THD *thd, TABLE **tables, uint count, uint flags)
write we must own metadata lock of MDL_SHARED_WRITE or stronger
type. For table to be locked for read we must own metadata lock
of MDL_SHARED_READ or stronger type).
The only exception are HANDLER statements which are allowed to
lock table for read while having only MDL_SHARED lock on it.
*/
DBUG_ASSERT(t->s->tmp_table ||
thd->mdl_context.is_lock_owner(MDL_key::TABLE,
t->s->db.str, t->s->table_name.str,
t->reginfo.lock_type >= TL_WRITE_ALLOW_WRITE ?
MDL_SHARED_WRITE : MDL_SHARED_READ) ||
(t->open_by_handler &&
thd->mdl_context.is_lock_owner(MDL_key::TABLE,
t->s->db.str, t->s->table_name.str,
MDL_SHARED)));
MDL_SHARED_WRITE : MDL_SHARED_READ));
/*
Prevent modifications to base tables if READ_ONLY is activated.

View File

@ -391,7 +391,11 @@ public:
virtual const bitmap_t *incompatible_waiting_types_bitmap() const
{ return m_waiting_incompatible; }
virtual bool needs_notification(const MDL_ticket *ticket) const
{ return (ticket->get_type() >= MDL_SHARED_NO_WRITE); }
{
return ticket->get_type() == MDL_SHARED_NO_WRITE ||
ticket->get_type() == MDL_SHARED_NO_READ_WRITE ||
ticket->get_type() == MDL_EXCLUSIVE;
}
/**
Notify threads holding a shared metadata locks on object which
@ -1413,7 +1417,8 @@ const MDL_lock::bitmap_t
MDL_lock::MDL_scoped_lock::m_granted_incompatible[MDL_TYPE_END]=
{
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_INTENTION_EXCLUSIVE), 0, 0, 0, 0, 0, 0,
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_INTENTION_EXCLUSIVE),
0, 0, 0, 0, 0, 0, 0,
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED) | MDL_BIT(MDL_INTENTION_EXCLUSIVE)
};
@ -1421,7 +1426,7 @@ const MDL_lock::bitmap_t
MDL_lock::MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END]=
{
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED),
MDL_BIT(MDL_EXCLUSIVE), 0, 0, 0, 0, 0, 0, 0
MDL_BIT(MDL_EXCLUSIVE), 0, 0, 0, 0, 0, 0, 0, 0
};
@ -1434,38 +1439,40 @@ MDL_lock::MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END]=
if there is granted lock of certain type.
Request | Granted requests for lock |
type | S SH SR SW SU SNW SNRW X |
----------+----------------------------------+
S | + + + + + + + - |
SH | + + + + + + + - |
SR | + + + + + + - - |
SW | + + + + + - - - |
SU | + + + + - - - - |
SNW | + + + - - - - - |
SNRW | + + - - - - - - |
X | - - - - - - - - |
SU -> X | - - - - 0 0 0 0 |
SNW -> X | - - - 0 0 0 0 0 |
SNRW -> X | - - 0 0 0 0 0 0 |
type | S SH SR SW SU SRO SNW SNRW X |
----------+---------------------------------------+
S | + + + + + + + + - |
SH | + + + + + + + + - |
SR | + + + + + + + - - |
SW | + + + + + - - - - |
SU | + + + + - + - - - |
SRO | + + + - + + + - - |
SNW | + + + - - + - - - |
SNRW | + + - - - - - - - |
X | - - - - - - - - - |
SU -> X | - - - - 0 - 0 0 0 |
SNW -> X | - - - 0 0 - 0 0 0 |
SNRW -> X | - - 0 0 0 0 0 0 0 |
The second array specifies if particular type of request can be satisfied
if there is waiting request for the same lock of certain type. In other
words it specifies what is the priority of different lock types.
Request | Pending requests for lock |
type | S SH SR SW SU SNW SNRW X |
----------+---------------------------------+
S | + + + + + + + - |
SH | + + + + + + + + |
SR | + + + + + + - - |
SW | + + + + + - - - |
SU | + + + + + + + - |
SNW | + + + + + + + - |
SNRW | + + + + + + + - |
X | + + + + + + + + |
SU -> X | + + + + + + + + |
SNW -> X | + + + + + + + + |
SNRW -> X | + + + + + + + + |
type | S SH SR SW SU SRO SNW SNRW X |
----------+--------------------------------------+
S | + + + + + + + + - |
SH | + + + + + + + + + |
SR | + + + + + + + - - |
SW | + + + + + + - - - |
SU | + + + + + + + + - |
SRO | + + + - + + + - - |
SNW | + + + + + + + + - |
SNRW | + + + + + + + + - |
X | + + + + + + + + + |
SU -> X | + + + + + + + + + |
SNW -> X | + + + + + + + + + |
SNRW -> X | + + + + + + + + + |
Here: "+" -- means that request can be satisfied
"-" -- means that request can't be satisfied and should wait
@ -1487,19 +1494,23 @@ MDL_lock::MDL_object_lock::m_granted_incompatible[MDL_TYPE_END]=
MDL_BIT(MDL_EXCLUSIVE),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
MDL_BIT(MDL_SHARED_NO_WRITE),
MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
MDL_BIT(MDL_SHARED_WRITE),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE) |
MDL_BIT(MDL_SHARED_WRITE),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE) |
MDL_BIT(MDL_SHARED_WRITE) | MDL_BIT(MDL_SHARED_READ),
MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY) |
MDL_BIT(MDL_SHARED_UPGRADABLE) | MDL_BIT(MDL_SHARED_WRITE) |
MDL_BIT(MDL_SHARED_READ),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE) |
MDL_BIT(MDL_SHARED_WRITE) | MDL_BIT(MDL_SHARED_READ) |
MDL_BIT(MDL_SHARED_HIGH_PRIO) | MDL_BIT(MDL_SHARED)
MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_READ_ONLY) |
MDL_BIT(MDL_SHARED_UPGRADABLE) | MDL_BIT(MDL_SHARED_WRITE) |
MDL_BIT(MDL_SHARED_READ) | MDL_BIT(MDL_SHARED_HIGH_PRIO) |
MDL_BIT(MDL_SHARED)
};
@ -1513,6 +1524,8 @@ MDL_lock::MDL_object_lock::m_waiting_incompatible[MDL_TYPE_END]=
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
MDL_BIT(MDL_SHARED_NO_WRITE),
MDL_BIT(MDL_EXCLUSIVE),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
MDL_BIT(MDL_SHARED_WRITE),
MDL_BIT(MDL_EXCLUSIVE),
MDL_BIT(MDL_EXCLUSIVE),
0
@ -2306,10 +2319,11 @@ MDL_context::upgrade_shared_lock(MDL_ticket *mdl_ticket,
if (mdl_ticket->has_stronger_or_equal_type(new_type))
DBUG_RETURN(FALSE);
/* Only allow upgrades from SHARED_UPGRADABLE/NO_WRITE/NO_READ_WRITE */
/* Only allow upgrades from SHARED_UPGRADABLE/NO_WRITE/NO_READ_WRITE/READ */
DBUG_ASSERT(mdl_ticket->m_type == MDL_SHARED_UPGRADABLE ||
mdl_ticket->m_type == MDL_SHARED_NO_WRITE ||
mdl_ticket->m_type == MDL_SHARED_NO_READ_WRITE);
mdl_ticket->m_type == MDL_SHARED_NO_READ_WRITE ||
mdl_ticket->m_type == MDL_SHARED_READ);
mdl_xlock_request.init(&mdl_ticket->m_lock->key, new_type,
MDL_TRANSACTION);

View File

@ -195,6 +195,12 @@ enum enum_mdl_type {
To be used for the first phase of ALTER TABLE.
*/
MDL_SHARED_UPGRADABLE,
/*
A shared metadata lock for cases when we need to read data from table
and block all concurrent modifications to it (for both data and metadata).
Used by LOCK TABLES READ statement.
*/
MDL_SHARED_READ_ONLY,
/*
An upgradable shared metadata lock which blocks all attempts to update
table data, allowing reads.
@ -467,6 +473,19 @@ public:
type= type_arg;
}
/**
Is this a request for a lock which allow data to be updated?
@note This method returns true for MDL_SHARED_UPGRADABLE type of
lock. Even though this type of lock doesn't allow updates
it will always be upgraded to one that does.
*/
bool is_write_lock_request() const
{
return (type >= MDL_SHARED_WRITE &&
type != MDL_SHARED_READ_ONLY);
}
/*
This is to work around the ugliness of TABLE_LIST
compiler-generated assignment operator. It is currently used

View File

@ -1495,7 +1495,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
Note that we allow write locks on log tables as otherwise logging
to general/slow log would be disabled in read only transactions.
*/
if (table_list->mdl_request.type >= MDL_SHARED_WRITE &&
if (table_list->mdl_request.is_write_lock_request() &&
thd->tx_read_only &&
!(flags & (MYSQL_LOCK_LOG_TABLE | MYSQL_OPEN_HAS_MDL_LOCK)))
{
@ -1654,7 +1654,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
pre-acquiring metadata locks at the beggining of
open_tables() call.
*/
if (table_list->mdl_request.type >= MDL_SHARED_WRITE &&
if (table_list->mdl_request.is_write_lock_request() &&
! (flags & (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
MYSQL_OPEN_FORCE_SHARED_MDL |
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
@ -2146,11 +2146,6 @@ Locked_tables_list::unlock_locked_tables(THD *thd)
request for metadata locks and TABLE_LIST elements.
*/
reset();
if (thd->variables.option_bits & OPTION_AUTOCOMMIT)
{
thd->variables.option_bits&= ~(OPTION_NOT_AUTOCOMMIT);
thd->server_status|= SERVER_STATUS_AUTOCOMMIT;
}
}
@ -3605,6 +3600,7 @@ lock_table_names(THD *thd, const DDL_options_st &options,
table= table->next_global)
{
if (table->mdl_request.type < MDL_SHARED_UPGRADABLE ||
table->mdl_request.type == MDL_SHARED_READ_ONLY ||
table->open_type == OT_TEMPORARY_ONLY ||
(table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table)))
{
@ -3728,6 +3724,11 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
for (table= tables_start; table && table != tables_end;
table= table->next_global)
{
/*
Check below needs to be updated if this function starts
called for SRO locks.
*/
DBUG_ASSERT(table->mdl_request.type != MDL_SHARED_READ_ONLY);
if (table->mdl_request.type < MDL_SHARED_UPGRADABLE ||
table->open_type == OT_TEMPORARY_ONLY ||
(table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table)))

View File

@ -650,6 +650,28 @@ bool Drop_table_error_handler::handle_condition(THD *thd,
}
/**
Handle an error from MDL_context::upgrade_lock() and mysql_lock_tables().
Ignore ER_LOCK_ABORTED and ER_LOCK_DEADLOCK errors.
*/
bool
MDL_deadlock_and_lock_abort_error_handler::
handle_condition(THD *thd,
uint sql_errno,
const char *sqlstate,
Sql_condition::enum_warning_level level,
const char* msg,
Sql_condition **cond_hdl)
{
*cond_hdl= NULL;
if (sql_errno == ER_LOCK_ABORTED || sql_errno == ER_LOCK_DEADLOCK)
m_need_reopen= true;
return m_need_reopen;
}
/**
Send timeout to thread.

View File

@ -1744,6 +1744,30 @@ private:
};
/**
Internal error handler to process an error from MDL_context::upgrade_lock()
and mysql_lock_tables(). Used by implementations of HANDLER READ and
LOCK TABLES LOCAL.
*/
class MDL_deadlock_and_lock_abort_error_handler: public Internal_error_handler
{
public:
virtual
bool handle_condition(THD *thd,
uint sql_errno,
const char *sqlstate,
Sql_condition::enum_warning_level level,
const char* msg,
Sql_condition **cond_hdl);
bool need_reopen() const { return m_need_reopen; };
void init() { m_need_reopen= FALSE; };
private:
bool m_need_reopen;
};
/**
Tables that were locked with LOCK TABLES statement.

View File

@ -485,56 +485,6 @@ bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
}
/**
A helper class to process an error from mysql_lock_tables().
HANDLER READ statement's attempt to lock the subject table
may get aborted if there is a pending DDL. In that case
we close the table, reopen it, and try to read again.
This is implicit and obscure, since HANDLER position
is lost in the process, but it's the legacy server
behaviour we should preserve.
*/
class Sql_handler_lock_error_handler: public Internal_error_handler
{
public:
virtual
bool handle_condition(THD *thd,
uint sql_errno,
const char *sqlstate,
Sql_condition::enum_warning_level level,
const char* msg,
Sql_condition **cond_hdl);
bool need_reopen() const { return m_need_reopen; };
void init() { m_need_reopen= FALSE; };
private:
bool m_need_reopen;
};
/**
Handle an error from mysql_lock_tables().
Ignore ER_LOCK_ABORTED errors.
*/
bool
Sql_handler_lock_error_handler::
handle_condition(THD *thd,
uint sql_errno,
const char *sqlstate,
Sql_condition::enum_warning_level level,
const char* msg,
Sql_condition **cond_hdl)
{
*cond_hdl= NULL;
if (sql_errno == ER_LOCK_ABORTED)
m_need_reopen= TRUE;
return m_need_reopen;
}
/**
Finds an open HANDLER table.
@ -731,7 +681,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
int error, keyno;
uint num_rows;
uchar *UNINIT_VAR(key);
Sql_handler_lock_error_handler sql_handler_lock_error;
MDL_deadlock_and_lock_abort_error_handler sql_handler_lock_error;
DBUG_ENTER("mysql_ha_read");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));

View File

@ -2728,27 +2728,76 @@ bool sp_process_definer(THD *thd)
static bool lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
{
Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
MDL_deadlock_and_lock_abort_error_handler deadlock_handler;
MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
uint counter;
TABLE_LIST *table;
thd->in_lock_tables= 1;
retry:
if (open_tables(thd, &tables, &counter, 0, &lock_tables_prelocking_strategy))
goto err;
for (table= tables; table; table= table->next_global)
{
if (!table->placeholder())
{
if (table->table->s->tmp_table)
{
/*
We allow to change temporary tables even if they were locked for read
by LOCK TABLES. To avoid a discrepancy between lock acquired at LOCK
TABLES time and by the statement which is later executed under LOCK TABLES
we ensure that for temporary tables we always request a write lock (such
discrepancy can cause problems for the storage engine).
We don't set TABLE_LIST::lock_type in this case as this might result in
extra warnings from THD::decide_logging_format() even though binary logging
is totally irrelevant for LOCK TABLES.
TABLES time and by the statement which is later executed under LOCK
TABLES we ensure that for temporary tables we always request a write
lock (such discrepancy can cause problems for the storage engine).
We don't set TABLE_LIST::lock_type in this case as this might result
in extra warnings from THD::decide_logging_format() even though
binary logging is totally irrelevant for LOCK TABLES.
*/
for (table= tables; table; table= table->next_global)
if (!table->placeholder() && table->table->s->tmp_table)
table->table->reginfo.lock_type= TL_WRITE;
}
else if (table->mdl_request.type == MDL_SHARED_READ &&
! table->prelocking_placeholder &&
table->table->file->lock_count() == 0)
{
/*
In case when LOCK TABLE ... READ LOCAL was issued for table with
storage engine which doesn't support READ LOCAL option and doesn't
use THR_LOCK locks we need to upgrade weak SR metadata lock acquired
in open_tables() to stronger SRO metadata lock.
This is not needed for tables used through stored routines or
triggers as we always acquire SRO (or even stronger SNRW) metadata
lock for them.
*/
deadlock_handler.init();
thd->push_internal_handler(&deadlock_handler);
bool result= thd->mdl_context.upgrade_shared_lock(
table->table->mdl_ticket,
MDL_SHARED_READ_ONLY,
thd->variables.lock_wait_timeout);
thd->pop_internal_handler();
if (deadlock_handler.need_reopen())
{
/*
Deadlock occurred during upgrade of metadata lock.
Let us restart acquring and opening tables for LOCK TABLES.
*/
close_tables_for_reopen(thd, &tables, mdl_savepoint);
if (thd->open_temporary_tables(tables))
goto err;
goto retry;
}
if (result)
goto err;
}
}
}
if (lock_tables(thd, tables, counter, 0) ||
thd->locked_tables_list.init_locked_tables(thd))
@ -4789,8 +4838,7 @@ end_with_restore_list:
if (lock_tables_precheck(thd, all_tables))
goto error;
thd->variables.option_bits|= OPTION_TABLE_LOCK | OPTION_NOT_AUTOCOMMIT;
thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT;
thd->variables.option_bits|= OPTION_TABLE_LOCK;
res= lock_tables_open_and_lock_tables(thd, all_tables);

View File

@ -46,6 +46,7 @@ a b
1 10
2 20
3 30
4 40
insert into t2 values (1);
ERROR HY000: Table 't2' was not locked with LOCK TABLES
commit;
@ -58,6 +59,7 @@ a b
1 10
2 20
3 30
4 40
select * from t2;
a
1
@ -70,6 +72,7 @@ a b
1 10
2 20
3 30
4 40
5 50
select * from t2;
a
@ -81,6 +84,7 @@ a b
1 10
2 20
3 30
4 40
5 50
select * from t2;
a

View File

@ -2,8 +2,8 @@ drop table if exists t1,t3;
CREATE TABLE t3(a int,c int,d int)engine=TOKUDB;
lock table t3 read;
create temporary table t1 engine=tokudb as SELECT 1;
unlock tables;
select * from t1;
1
1
unlock tables;
drop table t1,t3;

View File

@ -7,7 +7,7 @@ enable_warnings;
CREATE TABLE t3(a int,c int,d int)engine=TOKUDB;
lock table t3 read;
create temporary table t1 engine=tokudb as SELECT 1;
unlock tables;
select * from t1;
unlock tables;
drop table t1,t3;